summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/SCsub1
-rw-r--r--editor/debugger/SCsub5
-rw-r--r--editor/debugger/editor_debugger_inspector.cpp277
-rw-r--r--editor/debugger/editor_debugger_inspector.h98
-rw-r--r--editor/debugger/editor_debugger_node.cpp564
-rw-r--r--editor/debugger/editor_debugger_node.h176
-rw-r--r--editor/debugger/editor_debugger_tree.cpp274
-rw-r--r--editor/debugger/editor_debugger_tree.h74
-rw-r--r--editor/debugger/script_editor_debugger.cpp (renamed from editor/script_editor_debugger.cpp)1592
-rw-r--r--editor/debugger/script_editor_debugger.h (renamed from editor/script_editor_debugger.h)112
-rw-r--r--editor/editor_node.cpp50
-rw-r--r--editor/editor_node.h8
-rw-r--r--editor/editor_path.cpp2
-rw-r--r--editor/editor_run.cpp32
-rw-r--r--editor/editor_run.h8
-rw-r--r--editor/inspector_dock.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp18
-rw-r--r--editor/plugins/debugger_editor_plugin.cpp51
-rw-r--r--editor/plugins/debugger_editor_plugin.h50
-rw-r--r--editor/plugins/script_editor_plugin.cpp129
-rw-r--r--editor/plugins/script_editor_plugin.h14
-rw-r--r--editor/plugins/script_text_editor.cpp8
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp14
-rw-r--r--editor/scene_tree_dock.cpp32
-rw-r--r--editor/settings_config_dialog.cpp7
25 files changed, 2131 insertions, 1467 deletions
diff --git a/editor/SCsub b/editor/SCsub
index 2b560f68e8..4431166ee6 100644
--- a/editor/SCsub
+++ b/editor/SCsub
@@ -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')
diff --git a/editor/debugger/SCsub b/editor/debugger/SCsub
new file mode 100644
index 0000000000..2b1e889fb0
--- /dev/null
+++ b/editor/debugger/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import('env')
+
+env.add_source_files(env.editor_sources, "*.cpp")
diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp
new file mode 100644
index 0000000000..35d7dda3f4
--- /dev/null
+++ b/editor/debugger/editor_debugger_inspector.cpp
@@ -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);
+}
diff --git a/editor/debugger/editor_debugger_inspector.h b/editor/debugger/editor_debugger_inspector.h
new file mode 100644
index 0000000000..e1dfbefcf3
--- /dev/null
+++ b/editor/debugger/editor_debugger_inspector.h
@@ -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
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
new file mode 100644
index 0000000000..cf600312fe
--- /dev/null
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -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);
+ });
+}
diff --git a/editor/debugger/editor_debugger_node.h b/editor/debugger/editor_debugger_node.h
new file mode 100644
index 0000000000..df833d4f60
--- /dev/null
+++ b/editor/debugger/editor_debugger_node.h
@@ -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
diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp
new file mode 100644
index 0000000000..9ba5d0cbe1
--- /dev/null
+++ b/editor/debugger/editor_debugger_tree.cpp
@@ -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);
+}
diff --git a/editor/debugger/editor_debugger_tree.h b/editor/debugger/editor_debugger_tree.h
new file mode 100644
index 0000000000..d9084bc596
--- /dev/null
+++ b/editor/debugger/editor_debugger_tree.h
@@ -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
diff --git a/editor/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index cc8fc0a3b9..d113fe8718 100644
--- a/editor/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -32,18 +32,20 @@
#include "core/io/marshalls.h"
#include "core/project_settings.h"
+#include "core/script_debugger_remote.h"
#include "core/ustring.h"
#include "editor/editor_log.h"
#include "editor/editor_network_profiler.h"
+#include "editor/editor_node.h"
+#include "editor/editor_profiler.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
#include "editor/editor_visual_profiler.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/spatial_editor_plugin.h"
-#include "editor_node.h"
-#include "editor_profiler.h"
-#include "editor_scale.h"
-#include "editor_settings.h"
+#include "editor/property_editor.h"
#include "main/performance.h"
-#include "property_editor.h"
+#include "scene/debugger/scene_debugger.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
@@ -56,149 +58,14 @@
#include "scene/gui/tree.h"
#include "scene/resources/packed_scene.h"
-class ScriptEditorDebuggerVariables : public Object {
-
- GDCLASS(ScriptEditorDebuggerVariables, Object);
-
- List<PropertyInfo> props;
- Map<StringName, Variant> values;
-
-protected:
- bool _set(const StringName &p_name, const Variant &p_value) {
-
- return false;
- }
-
- bool _get(const StringName &p_name, Variant &r_ret) const {
-
- if (!values.has(p_name))
- return false;
- r_ret = values[p_name];
- return true;
- }
- void _get_property_list(List<PropertyInfo> *p_list) const {
-
- for (const List<PropertyInfo>::Element *E = props.front(); E; E = E->next())
- p_list->push_back(E->get());
- }
-
-public:
- void clear() {
-
- props.clear();
- values.clear();
- }
-
- String get_var_value(const String &p_var) const {
-
- for (Map<StringName, Variant>::Element *E = values.front(); E; E = E->next()) {
- String v = E->key().operator String().get_slice("/", 1);
- if (v == p_var)
- return E->get();
- }
-
- return "";
- }
-
- void add_property(const String &p_name, const Variant &p_value, const PropertyHint &p_hint, const String p_hint_string) {
-
- PropertyInfo pinfo;
- pinfo.name = p_name;
- pinfo.type = p_value.get_type();
- pinfo.hint = p_hint;
- pinfo.hint_string = p_hint_string;
- props.push_back(pinfo);
- values[p_name] = p_value;
- }
-
- void update() {
- _change_notify();
- }
-
- ScriptEditorDebuggerVariables() {
- }
-};
-
-class ScriptEditorDebuggerInspectedObject : public Object {
-
- GDCLASS(ScriptEditorDebuggerInspectedObject, Object);
-
-protected:
- bool _set(const StringName &p_name, const Variant &p_value) {
-
- if (!prop_values.has(p_name) || String(p_name).begins_with("Constants/"))
- return false;
-
- prop_values[p_name] = p_value;
- emit_signal("value_edited", p_name, p_value);
- return true;
- }
-
- bool _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 _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());
- }
- }
-
- static void _bind_methods() {
-
- ClassDB::bind_method(D_METHOD("get_title"), &ScriptEditorDebuggerInspectedObject::get_title);
- ClassDB::bind_method(D_METHOD("get_variant"), &ScriptEditorDebuggerInspectedObject::get_variant);
- ClassDB::bind_method(D_METHOD("clear"), &ScriptEditorDebuggerInspectedObject::clear);
- ClassDB::bind_method(D_METHOD("get_remote_object_id"), &ScriptEditorDebuggerInspectedObject::get_remote_object_id);
-
- ADD_SIGNAL(MethodInfo("value_edited"));
- }
-
-public:
- String type_name;
- ObjectID remote_object_id;
- List<PropertyInfo> prop_list;
- Map<StringName, Variant> prop_values;
-
- ObjectID get_remote_object_id() {
- return remote_object_id;
- }
-
- String get_title() {
- if (remote_object_id.is_valid())
- return TTR("Remote ") + String(type_name) + ": " + itos(remote_object_id);
- else
- return "<null>";
- }
- Variant get_variant(const StringName &p_name) {
-
- Variant var;
- _get(p_name, var);
- return var;
- }
-
- void clear() {
-
- prop_list.clear();
- prop_values.clear();
- }
- void update() {
- _change_notify();
- }
- void update_single(const char *p_prop) {
- _change_notify(p_prop);
- }
-
- ScriptEditorDebuggerInspectedObject() {
+void ScriptEditorDebugger::_put_msg(String p_message, Array p_data) {
+ if (is_session_active()) {
+ Array msg;
+ msg.push_back(p_message);
+ msg.push_back(p_data);
+ ppeer->put_var(msg);
}
-};
+}
void ScriptEditorDebugger::debug_copy() {
String msg = reason->get_text();
@@ -213,285 +80,149 @@ void ScriptEditorDebugger::debug_skip_breakpoints() {
else
skip_breakpoints->set_icon(get_icon("DebugSkipBreakpointsOff", "EditorIcons"));
- if (connection.is_valid()) {
- Array msg;
- msg.push_back("set_skip_breakpoints");
- msg.push_back(skip_breakpoints_value);
- ppeer->put_var(msg);
- }
+ Array msg;
+ msg.push_back(skip_breakpoints_value);
+ _put_msg("set_skip_breakpoints", msg);
}
void ScriptEditorDebugger::debug_next() {
ERR_FAIL_COND(!breaked);
- ERR_FAIL_COND(connection.is_null());
- ERR_FAIL_COND(!connection->is_connected_to_host());
- Array msg;
- msg.push_back("next");
- ppeer->put_var(msg);
+
+ _put_msg("next", Array());
_clear_execution();
- stack_dump->clear();
}
void ScriptEditorDebugger::debug_step() {
ERR_FAIL_COND(!breaked);
- ERR_FAIL_COND(connection.is_null());
- ERR_FAIL_COND(!connection->is_connected_to_host());
- Array msg;
- msg.push_back("step");
- ppeer->put_var(msg);
+ _put_msg("step", Array());
_clear_execution();
- stack_dump->clear();
}
void ScriptEditorDebugger::debug_break() {
ERR_FAIL_COND(breaked);
- ERR_FAIL_COND(connection.is_null());
- ERR_FAIL_COND(!connection->is_connected_to_host());
- Array msg;
- msg.push_back("break");
- ppeer->put_var(msg);
+ _put_msg("break", Array());
}
void ScriptEditorDebugger::debug_continue() {
ERR_FAIL_COND(!breaked);
- ERR_FAIL_COND(connection.is_null());
- ERR_FAIL_COND(!connection->is_connected_to_host());
- OS::get_singleton()->enable_for_stealing_focus(EditorNode::get_singleton()->get_child_process_id());
+ // Allow focus stealing only if we actually run this client for security.
+ if (remote_pid && EditorNode::get_singleton()->has_child_process(remote_pid))
+ OS::get_singleton()->enable_for_stealing_focus(remote_pid);
- Array msg;
_clear_execution();
- msg.push_back("continue");
- ppeer->put_var(msg);
+ _put_msg("continue", Array());
}
-void ScriptEditorDebugger::_scene_tree_folded(Object *obj) {
-
- if (updating_scene_tree) {
-
- return;
- }
- TreeItem *item = Object::cast_to<TreeItem>(obj);
-
- if (!item)
- return;
-
- ObjectID id = item->get_metadata(0);
- if (unfold_cache.has(id)) {
- unfold_cache.erase(id);
+void ScriptEditorDebugger::update_tabs() {
+ if (error_count == 0 && warning_count == 0) {
+ errors_tab->set_name(TTR("Errors"));
+ tabs->set_tab_icon(errors_tab->get_index(), Ref<Texture2D>());
} else {
- unfold_cache.insert(id);
+ errors_tab->set_name(TTR("Errors") + " (" + itos(error_count + warning_count) + ")");
+ if (error_count == 0) {
+ tabs->set_tab_icon(errors_tab->get_index(), get_icon("Warning", "EditorIcons"));
+ } else {
+ tabs->set_tab_icon(errors_tab->get_index(), get_icon("Error", "EditorIcons"));
+ }
}
}
-void ScriptEditorDebugger::_scene_tree_selected() {
+void ScriptEditorDebugger::save_node(ObjectID p_id, const String &p_file) {
+ Array msg;
+ msg.push_back(p_id);
+ msg.push_back(p_file);
+ _put_msg("save_node", msg);
+}
- if (updating_scene_tree) {
+void ScriptEditorDebugger::_file_selected(const String &p_file) {
+ Error err;
+ FileAccessRef file = FileAccess::open(p_file, FileAccess::WRITE, &err);
+ if (err != OK) {
+ ERR_PRINT("Failed to open " + p_file);
return;
}
- TreeItem *item = inspect_scene_tree->get_selected();
- if (!item) {
+ Vector<String> line;
+ line.resize(Performance::MONITOR_MAX);
- return;
+ // signatures
+ for (int i = 0; i < Performance::MONITOR_MAX; i++) {
+ line.write[i] = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i));
}
+ file->store_csv_line(line);
- inspected_object_id = item->get_metadata(0).operator ObjectID();
-
- Array msg;
- msg.push_back("inspect_object");
- msg.push_back(inspected_object_id);
- ppeer->put_var(msg);
-}
+ // values
+ List<Vector<float> >::Element *E = perf_history.back();
+ while (E) {
-void ScriptEditorDebugger::_scene_tree_rmb_selected(const Vector2 &p_position) {
+ Vector<float> &perf_data = E->get();
+ for (int i = 0; i < perf_data.size(); i++) {
- TreeItem *item = inspect_scene_tree->get_item_at_position(p_position);
- if (!item)
- return;
-
- item->select(0);
+ line.write[i] = String::num_real(perf_data[i]);
+ }
+ file->store_csv_line(line);
+ E = E->prev();
+ }
+ file->store_string("\n");
- 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();
+ Vector<Vector<String> > profiler_data = profiler->get_data_as_csv();
+ for (int i = 0; i < profiler_data.size(); i++) {
+ file->store_csv_line(profiler_data[i]);
+ }
}
-void ScriptEditorDebugger::_file_selected(const String &p_file) {
- switch (file_dialog_mode) {
- case SAVE_NODE: {
- Array msg;
- msg.push_back("save_node");
- msg.push_back(inspected_object_id);
- msg.push_back(p_file);
- ppeer->put_var(msg);
- } break;
- case SAVE_CSV: {
- Error err;
- FileAccessRef file = FileAccess::open(p_file, FileAccess::WRITE, &err);
+void ScriptEditorDebugger::request_remote_tree() {
- if (err != OK) {
- ERR_PRINT("Failed to open " + p_file);
- return;
- }
- Vector<String> line;
- line.resize(Performance::MONITOR_MAX);
-
- // signatures
- for (int i = 0; i < Performance::MONITOR_MAX; i++) {
- line.write[i] = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i));
- }
- file->store_csv_line(line);
-
- // values
- List<Vector<float> >::Element *E = perf_history.back();
- while (E) {
-
- Vector<float> &perf_data = E->get();
- for (int i = 0; i < perf_data.size(); i++) {
-
- line.write[i] = String::num_real(perf_data[i]);
- }
- file->store_csv_line(line);
- E = E->prev();
- }
- file->store_string("\n");
-
- Vector<Vector<String> > profiler_data = profiler->get_data_as_csv();
- for (int i = 0; i < profiler_data.size(); i++) {
- file->store_csv_line(profiler_data[i]);
- }
+ _put_msg("request_scene_tree", Array());
+}
- } break;
- }
+const SceneDebuggerTree *ScriptEditorDebugger::get_remote_tree() {
+ return scene_tree;
}
-void ScriptEditorDebugger::_scene_tree_property_value_edited(const String &p_prop, const Variant &p_value) {
+void ScriptEditorDebugger::update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value) {
Array msg;
- msg.push_back("set_object_property");
- msg.push_back(inspected_object_id);
+ msg.push_back(p_obj_id);
msg.push_back(p_prop);
msg.push_back(p_value);
- ppeer->put_var(msg);
- inspect_edited_object_timeout = 0.7; //avoid annoyance, don't request soon after editing
+ _put_msg("set_object_property", msg);
}
-void ScriptEditorDebugger::_scene_tree_property_select_object(ObjectID p_object) {
+void ScriptEditorDebugger::request_remote_object(ObjectID p_obj_id) {
- inspected_object_id = p_object;
+ ERR_FAIL_COND(p_obj_id.is_null());
Array msg;
- msg.push_back("inspect_object");
- msg.push_back(inspected_object_id);
- ppeer->put_var(msg);
+ msg.push_back(p_obj_id);
+ _put_msg("inspect_object", msg);
}
-void ScriptEditorDebugger::_scene_tree_request() {
-
- ERR_FAIL_COND(connection.is_null());
- ERR_FAIL_COND(!connection->is_connected_to_host());
-
- Array msg;
- msg.push_back("request_scene_tree");
- ppeer->put_var(msg);
-}
-
-/// Populates inspect_scene_tree recursively given data in nodes.
-/// Nodes is an array containing 4 elements for each node, it follows this pattern:
-/// nodes[i] == number of direct children of this node
-/// nodes[i + 1] == node name
-/// nodes[i + 2] == node class
-/// nodes[i + 3] == node instance id
-///
-/// Returns the number of items parsed in nodes from current_index.
-///
-/// 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
-///
-int ScriptEditorDebugger::_update_scene_tree(TreeItem *parent, const Array &nodes, int current_index) {
- String filter = EditorNode::get_singleton()->get_scene_tree_dock()->get_filter();
- String item_text = nodes[current_index + 1];
- String item_type = nodes[current_index + 2];
- bool keep = filter.is_subsequence_ofi(item_text);
-
- TreeItem *item = inspect_scene_tree->create_item(parent);
- item->set_text(0, item_text);
- item->set_tooltip(0, TTR("Type:") + " " + item_type);
- ObjectID id = nodes[current_index + 3].operator ObjectID();
- Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(nodes[current_index + 2], "");
- if (icon.is_valid()) {
- item->set_icon(0, icon);
- }
- item->set_metadata(0, id);
-
- if (id == inspected_object_id) {
- TreeItem *cti = item->get_parent();
- while (cti) {
- cti->set_collapsed(false);
- cti = cti->get_parent();
- }
- item->select(0);
- }
-
- // Set current item as collapsed if necessary
- if (parent) {
- if (!unfold_cache.has(id)) {
- item->set_collapsed(true);
- }
- }
+Object *ScriptEditorDebugger::get_remote_object(ObjectID p_id) {
+ return inspector->get_object(p_id);
+}
- int children_count = nodes[current_index];
- // Tracks the total number of items parsed in nodes, this is used to skips nodes that
- // are not direct children of the current node since we can't know in advance the total
- // number of children, direct and not, of a node without traversing the nodes array previously.
- // Keeping track of this allows us to build our remote scene tree by traversing the node
- // array just once.
- int items_count = 1;
- for (int i = 0; i < children_count; i++) {
- // Called for each direct child of item.
- // Direct children of current item might not be adjacent so items_count must
- // be incremented by the number of items parsed until now, otherwise we would not
- // be able to access the next child of the current item.
- // items_count is multiplied by 4 since that's the number of elements in the nodes
- // array needed to represent a single node.
- items_count += _update_scene_tree(item, nodes, current_index + items_count * 4);
- }
+void ScriptEditorDebugger::_remote_object_selected(ObjectID p_id) {
+ emit_signal("remote_object_requested", p_id);
+}
- // If item has not children and should not be kept delete it
- if (!keep && !item->get_children() && parent) {
- parent->remove_child(item);
- memdelete(item);
- }
+void ScriptEditorDebugger::_remote_object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value) {
+ update_remote_object(p_id, p_prop, p_value);
+ request_remote_object(p_id);
+}
- return items_count;
+void ScriptEditorDebugger::_remote_object_property_updated(ObjectID p_id, const String &p_property) {
+ emit_signal("remote_object_property_updated", p_id, p_property);
}
void ScriptEditorDebugger::_video_mem_request() {
- if (connection.is_null() || !connection->is_connected_to_host()) {
- // Video RAM usage is only available while a project is being debugged.
- return;
- }
-
- Array msg;
- msg.push_back("request_video_mem");
- ppeer->put_var(msg);
+ _put_msg("request_video_mem", Array());
}
Size2 ScriptEditorDebugger::get_minimum_size() const {
@@ -504,184 +235,72 @@ Size2 ScriptEditorDebugger::get_minimum_size() const {
void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_data) {
if (p_msg == "debug_enter") {
- Array msg;
- msg.push_back("get_stack_dump");
- ppeer->put_var(msg);
+
+ _put_msg("get_stack_dump", Array());
+
ERR_FAIL_COND(p_data.size() != 2);
bool can_continue = p_data[0];
String error = p_data[1];
- step->set_disabled(!can_continue);
- next->set_disabled(!can_continue);
- _set_reason_text(error, MESSAGE_ERROR);
- copy->set_disabled(false);
breaked = true;
- dobreak->set_disabled(true);
- docontinue->set_disabled(false);
+ can_debug = can_continue;
+ _update_buttons_state();
+ _set_reason_text(error, MESSAGE_ERROR);
emit_signal("breaked", true, can_continue);
OS::get_singleton()->move_window_to_foreground();
if (error != "") {
tabs->set_current_tab(0);
}
profiler->set_enabled(false);
- EditorNode::get_singleton()->get_pause_button()->set_pressed(true);
- EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
- _clear_remote_objects();
+ inspector->clear_cache(); // Take a chance to force remote objects update.
} else if (p_msg == "debug_exit") {
breaked = false;
+ can_debug = false;
_clear_execution();
- copy->set_disabled(true);
- step->set_disabled(true);
- next->set_disabled(true);
- reason->set_text("");
- reason->set_tooltip("");
- back->set_disabled(true);
- forward->set_disabled(true);
- dobreak->set_disabled(false);
- docontinue->set_disabled(true);
- emit_signal("breaked", false, false, Variant());
+ _update_buttons_state();
+ _set_reason_text(TTR("Execution resumed."), MESSAGE_SUCCESS);
+ emit_signal("breaked", false, false);
profiler->set_enabled(true);
profiler->disable_seeking();
- EditorNode::get_singleton()->get_pause_button()->set_pressed(false);
+ } else if (p_msg == "message:set_pid") {
+
+ ERR_FAIL_COND(p_data.size() < 1);
+ remote_pid = p_data[0];
} else if (p_msg == "message:click_ctrl") {
+ ERR_FAIL_COND(p_data.size() < 2);
clicked_ctrl->set_text(p_data[0]);
clicked_ctrl_type->set_text(p_data[1]);
-
} else if (p_msg == "message:scene_tree") {
- inspect_scene_tree->clear();
- Map<int, TreeItem *> lv;
-
- updating_scene_tree = true;
-
- _update_scene_tree(NULL, p_data, 0);
-
- updating_scene_tree = false;
-
- le_clear->set_disabled(false);
- le_set->set_disabled(false);
+ scene_tree->nodes.clear();
+ scene_tree->deserialize(p_data);
+ emit_signal("remote_tree_updated");
+ _update_buttons_state();
} else if (p_msg == "message:inspect_object") {
- ScriptEditorDebuggerInspectedObject *debugObj = NULL;
-
- ObjectID id = p_data[0];
- String type = p_data[1];
- Array properties = p_data[2];
-
- if (remote_objects.has(id)) {
- debugObj = remote_objects[id];
- } else {
- debugObj = memnew(ScriptEditorDebuggerInspectedObject);
- debugObj->remote_object_id = id;
- debugObj->type_name = type;
- remote_objects[id] = debugObj;
- debugObj->connect_compat("value_edited", this, "_scene_tree_property_value_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 < properties.size(); i++) {
-
- Array prop = properties[i];
- if (prop.size() != 6)
- continue;
-
- 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::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);
- }
- }
- }
- } 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";
- }
- }
- }
-
- //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 (editor->get_editor_history()->get_current() != debugObj->get_instance_id()) {
- editor->push_item(debugObj, "");
- } else {
-
- 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()) {
- EditorNode::get_singleton()->get_inspector()->update_property(E->get());
- }
- } else {
- //full update, because props were added or removed
- debugObj->update();
- }
- }
+ ObjectID id = inspector->add_object(p_data);
+ if (id.is_valid())
+ emit_signal("remote_object_updated", id);
} else if (p_msg == "message:video_mem") {
vmem_tree->clear();
TreeItem *root = vmem_tree->create_item();
+ ScriptDebuggerRemote::ResourceUsage usage;
+ usage.deserialize(p_data);
int total = 0;
- for (int i = 0; i < p_data.size(); i += 4) {
+ for (List<ScriptDebuggerRemote::ResourceInfo>::Element *E = usage.infos.front(); E; E = E->next()) {
TreeItem *it = vmem_tree->create_item(root);
- String type = p_data[i + 1];
- int bytes = p_data[i + 3].operator int();
- it->set_text(0, p_data[i + 0]); //path
- it->set_text(1, type); //type
- it->set_text(2, p_data[i + 2]); //type
- it->set_text(3, String::humanize_size(bytes)); //type
+ String type = E->get().type;
+ int bytes = E->get().vram;
+ it->set_text(0, E->get().path);
+ it->set_text(1, type);
+ it->set_text(2, E->get().format);
+ it->set_text(3, String::humanize_size(bytes));
total += bytes;
if (has_icon(type, "EditorIcons"))
@@ -693,18 +312,21 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
} else if (p_msg == "stack_dump") {
+ ScriptDebuggerRemote::ScriptStackDump stack;
+ stack.deserialize(p_data);
+
stack_dump->clear();
+ inspector->clear_stack_variables();
TreeItem *r = stack_dump->create_item();
- for (int i = 0; i < p_data.size(); i++) {
+ for (int i = 0; i < stack.frames.size(); i++) {
- Dictionary d = p_data[i];
- ERR_CONTINUE(!d.has("function"));
- ERR_CONTINUE(!d.has("file"));
- ERR_CONTINUE(!d.has("line"));
- ERR_CONTINUE(!d.has("id"));
TreeItem *s = stack_dump->create_item(r);
+ Dictionary d;
d["frame"] = i;
+ d["file"] = stack.frames[i].file;
+ d["function"] = stack.frames[i].func;
+ d["line"] = stack.frames[i].line;
s->set_metadata(0, d);
String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]) + " - at function: " + d["function"];
@@ -715,97 +337,22 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
}
} else if (p_msg == "stack_frame_vars") {
- variables->clear();
-
- int ofs = 0;
- int mcount = p_data[ofs];
- ofs++;
- for (int i = 0; i < mcount; i++) {
+ inspector->clear_stack_variables();
- String n = p_data[ofs + i * 2 + 0];
- Variant v = p_data[ofs + i * 2 + 1];
+ } else if (p_msg == "stack_frame_var") {
- 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";
- }
-
- variables->add_property("Locals/" + n, v, h, hs);
- }
-
- ofs += mcount * 2;
- mcount = p_data[ofs];
- ofs++;
- for (int i = 0; i < mcount; i++) {
-
- String n = p_data[ofs + i * 2 + 0];
- Variant v = p_data[ofs + i * 2 + 1];
- 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";
- }
-
- variables->add_property("Members/" + n, v, h, hs);
-
- if (n == "self") {
- _scene_tree_property_select_object(v);
- }
- }
-
- ofs += mcount * 2;
- mcount = p_data[ofs];
- ofs++;
- for (int i = 0; i < mcount; i++) {
-
- String n = p_data[ofs + i * 2 + 0];
- Variant v = p_data[ofs + i * 2 + 1];
- 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";
- }
-
- variables->add_property("Globals/" + n, v, h, hs);
- }
-
- variables->update();
- inspector->edit(variables);
+ inspector->add_stack_variable(p_data);
} else if (p_msg == "output") {
-
- //OUT
- for (int i = 0; i < p_data.size(); i++) {
-
- String t = p_data[i];
- //LOG
-
- if (!EditorNode::get_log()->is_visible()) {
- if (EditorNode::get_singleton()->are_bottom_panels_hidden()) {
- if (EDITOR_GET("run/output/always_open_output_on_play")) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(EditorNode::get_log());
- }
- }
- }
- EditorNode::get_log()->add_message(t);
- }
+ ERR_FAIL_COND(p_data.size() < 1);
+ String t = p_data[0];
+ EditorNode::get_log()->add_message(t);
} else if (p_msg == "performance") {
- Array arr = p_data[0];
Vector<float> p;
- p.resize(arr.size());
- for (int i = 0; i < arr.size(); i++) {
- p.write[i] = arr[i];
+ p.resize(p_data.size());
+ for (int i = 0; i < p_data.size(); i++) {
+ p.write[i] = p_data[i];
if (i < perf_items.size()) {
const float value = p[i];
@@ -833,7 +380,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
}
perf_history.push_front(p);
perf_draw->update();
+
} else if (p_msg == "visual_profile") {
+ // TODO check me.
uint64_t frame = p_data[0];
Vector<String> names = p_data[1];
Vector<real_t> values = p_data[2];
@@ -857,42 +406,29 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
areas_ptr[i].gpu_time = rr[i * 2 + 1];
}
}
-
visual_profiler->add_frame_metric(metric);
} else if (p_msg == "error") {
- // Should have at least two elements, error array and stack items count.
- ERR_FAIL_COND_MSG(p_data.size() < 2, "Malformed error message from script debugger.");
-
- // Error or warning data.
- Array err = p_data[0];
- ERR_FAIL_COND_MSG(err.size() < 10, "Malformed error message from script debugger.");
+ ScriptDebuggerRemote::OutputError oe;
+ ERR_FAIL_COND_MSG(oe.deserialize(p_data) == false, "Failed to deserialize error message");
// Format time.
Array time_vals;
- time_vals.push_back(err[0]);
- time_vals.push_back(err[1]);
- time_vals.push_back(err[2]);
- time_vals.push_back(err[3]);
+ time_vals.push_back(oe.hr);
+ time_vals.push_back(oe.min);
+ time_vals.push_back(oe.sec);
+ time_vals.push_back(oe.msec);
bool e;
- String time = String("%d:%02d:%02d.%03d").sprintf(time_vals, &e);
+ String time = String("%d:%02d:%02d:%04d").sprintf(time_vals, &e);
// Rest of the error data.
- String method = err[4];
- String source_file = err[5];
- String source_line = err[6];
- String error_cond = err[7];
- String error_msg = err[8];
- bool is_warning = err[9];
- bool has_method = !method.empty();
- bool has_error_msg = !error_msg.empty();
- bool source_is_project_file = source_file.begins_with("res://");
+ bool source_is_project_file = oe.source_file.begins_with("res://");
// Metadata to highlight error line in scripts.
Array source_meta;
- source_meta.push_back(source_file);
- source_meta.push_back(source_line);
+ source_meta.push_back(oe.source_file);
+ source_meta.push_back(oe.source_line);
// Create error tree to display above error or warning details.
TreeItem *r = error_tree->get_root();
@@ -902,40 +438,42 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
// Also provide the relevant details as tooltip to quickly check without
// uncollapsing the tree.
- String tooltip = is_warning ? TTR("Warning:") : TTR("Error:");
+ String tooltip = oe.warning ? TTR("Warning:") : TTR("Error:");
TreeItem *error = error_tree->create_item(r);
error->set_collapsed(true);
- error->set_icon(0, get_icon(is_warning ? "Warning" : "Error", "EditorIcons"));
+ error->set_icon(0, get_icon(oe.warning ? "Warning" : "Error", "EditorIcons"));
error->set_text(0, time);
error->set_text_align(0, TreeItem::ALIGN_LEFT);
String error_title;
// Include method name, when given, in error title.
- if (has_method)
- error_title += method + ": ";
+ if (!oe.source_func.empty())
+ error_title += oe.source_func + ": ";
// If we have a (custom) error message, use it as title, and add a C++ Error
// item with the original error condition.
- error_title += error_msg.empty() ? error_cond : error_msg;
+ error_title += oe.error_descr.empty() ? oe.error : oe.error_descr;
error->set_text(1, error_title);
tooltip += " " + error_title + "\n";
- if (has_error_msg) {
+ if (!oe.error_descr.empty()) {
// Add item for C++ error condition.
TreeItem *cpp_cond = error_tree->create_item(error);
cpp_cond->set_text(0, "<" + TTR("C++ Error") + ">");
- cpp_cond->set_text(1, error_cond);
+ cpp_cond->set_text(1, oe.error);
cpp_cond->set_text_align(0, TreeItem::ALIGN_LEFT);
- tooltip += TTR("C++ Error:") + " " + error_cond + "\n";
+ tooltip += TTR("C++ Error:") + " " + oe.error + "\n";
if (source_is_project_file)
cpp_cond->set_metadata(0, source_meta);
}
+ Vector<uint8_t> v;
+ v.resize(100);
// Source of the error.
- String source_txt = (source_is_project_file ? source_file.get_file() : source_file) + ":" + source_line;
- if (has_method)
- source_txt += " @ " + method + "()";
+ String source_txt = (source_is_project_file ? oe.source_file.get_file() : oe.source_file) + ":" + itos(oe.source_line);
+ if (!oe.source_func.empty())
+ source_txt += " @ " + oe.source_func + "()";
TreeItem *cpp_source = error_tree->create_item(error);
cpp_source->set_text(0, "<" + (source_is_project_file ? TTR("Source") : TTR("C++ Source")) + ">");
@@ -955,17 +493,14 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
// Format stack trace.
// stack_items_count is the number of elements to parse, with 3 items per frame
// of the stack trace (script, method, line).
- int stack_items_count = p_data[1];
+ const ScriptLanguage::StackInfo *infos = oe.callstack.ptr();
+ for (unsigned int i = 0; i < (unsigned int)oe.callstack.size(); i++) {
- for (int i = 0; i < stack_items_count; i += 3) {
- String script = p_data[2 + i];
- String method2 = p_data[3 + i];
- int line = p_data[4 + i];
TreeItem *stack_trace = error_tree->create_item(error);
Array meta;
- meta.push_back(script);
- meta.push_back(line);
+ meta.push_back(infos[i].file);
+ meta.push_back(infos[i].line);
stack_trace->set_metadata(0, meta);
if (i == 0) {
@@ -973,29 +508,32 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
stack_trace->set_text_align(0, TreeItem::ALIGN_LEFT);
error->set_metadata(0, meta);
}
- stack_trace->set_text(1, script.get_file() + ":" + itos(line) + " @ " + method2 + "()");
+ stack_trace->set_text(1, infos[i].file.get_file() + ":" + itos(infos[i].line) + " @ " + infos[i].func + "()");
}
- if (is_warning)
+ if (oe.warning)
warning_count++;
else
error_count++;
} else if (p_msg == "profile_sig") {
- //cache a signature
- profiler_signature[p_data[1]] = p_data[0];
+ // Cache a profiler signature.
+ ScriptDebuggerRemote::ProfilerSignature sig;
+ sig.deserialize(p_data);
+ profiler_signature[sig.id] = sig.name;
} else if (p_msg == "profile_frame" || p_msg == "profile_total") {
-
EditorProfiler::Metric metric;
+ ScriptDebuggerRemote::ProfilerFrame frame;
+ frame.deserialize(p_data);
metric.valid = true;
- metric.frame_number = p_data[0];
- metric.frame_time = p_data[1];
- metric.idle_time = p_data[2];
- metric.physics_time = p_data[3];
- metric.physics_frame_time = p_data[4];
- int frame_data_amount = p_data[6];
- int frame_function_amount = p_data[7];
+ metric.frame_number = frame.frame_number;
+ metric.frame_time = frame.frame_time;
+ metric.idle_time = frame.idle_time;
+ metric.physics_time = frame.physics_time;
+ metric.physics_frame_time = frame.physics_frame_time;
+ int frame_data_amount = frame.frames_data.size();
+ int frame_function_amount = frame.frame_functions.size();
if (frame_data_amount) {
EditorProfiler::Metric::Category frame_time;
@@ -1031,12 +569,11 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
metric.categories.push_back(frame_time);
}
- int idx = 8;
for (int i = 0; i < frame_data_amount; i++) {
EditorProfiler::Metric::Category c;
- String name = p_data[idx++];
- Array values = p_data[idx++];
+ String name = frame.frames_data[i].name;
+ Array values = frame.frames_data[i].data;
c.name = name.capitalize();
c.items.resize(values.size() / 2);
c.total_time = 0;
@@ -1058,16 +595,16 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
}
EditorProfiler::Metric::Category funcs;
- funcs.total_time = p_data[5]; //script time
+ funcs.total_time = frame.script_time;
funcs.items.resize(frame_function_amount);
funcs.name = "Script Functions";
funcs.signature = "script_functions";
for (int i = 0; i < frame_function_amount; i++) {
- int signature = p_data[idx++];
- int calls = p_data[idx++];
- float total = p_data[idx++];
- float self = p_data[idx++];
+ int signature = frame.frame_functions[i].sig_id;
+ int calls = frame.frame_functions[i].call_count;
+ float total = frame.frame_functions[i].total_time;
+ float self = frame.frame_functions[i].self_time;
EditorProfiler::Metric::Category::Item item;
if (profiler_signature.has(signature)) {
@@ -1102,23 +639,20 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
profiler->add_frame_metric(metric, false);
else
profiler->add_frame_metric(metric, true);
+
} else if (p_msg == "network_profile") {
- int frame_size = 6;
- for (int i = 0; i < p_data.size(); i += frame_size) {
- MultiplayerAPI::ProfilingInfo pi;
- pi.node = p_data[i + 0].operator ObjectID();
- pi.node_path = p_data[i + 1];
- pi.incoming_rpc = p_data[i + 2];
- pi.incoming_rset = p_data[i + 3];
- pi.outgoing_rpc = p_data[i + 4];
- pi.outgoing_rset = p_data[i + 5];
- network_profiler->add_node_frame_data(pi);
+ ScriptDebuggerRemote::NetworkProfilerFrame frame;
+ frame.deserialize(p_data);
+ for (int i = 0; i < frame.infos.size(); i++) {
+ network_profiler->add_node_frame_data(frame.infos[i]);
}
} else if (p_msg == "network_bandwidth") {
+ ERR_FAIL_COND(p_data.size() < 2);
network_profiler->set_bandwidth(p_data[0], p_data[1]);
} else if (p_msg == "kill_me") {
- editor->call_deferred("stop_child_process");
+ emit_signal("stop_requested");
+ _stop_and_notify();
}
}
@@ -1218,14 +752,11 @@ void ScriptEditorDebugger::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
- inspector->edit(variables);
skip_breakpoints->set_icon(get_icon("DebugSkipBreakpointsOff", "EditorIcons"));
copy->set_icon(get_icon("ActionCopy", "EditorIcons"));
step->set_icon(get_icon("DebugStep", "EditorIcons"));
next->set_icon(get_icon("DebugNext", "EditorIcons"));
- back->set_icon(get_icon("Back", "EditorIcons"));
- forward->set_icon(get_icon("Forward", "EditorIcons"));
dobreak->set_icon(get_icon("Pause", "EditorIcons"));
docontinue->set_icon(get_icon("DebugContinue", "EditorIcons"));
le_set->connect_compat("pressed", this, "_live_edit_set");
@@ -1239,31 +770,7 @@ void ScriptEditorDebugger::_notification(int p_what) {
} break;
case NOTIFICATION_PROCESS: {
- if (connection.is_valid()) {
-
- inspect_scene_tree_timeout -= get_process_delta_time();
- if (inspect_scene_tree_timeout < 0) {
- inspect_scene_tree_timeout = EditorSettings::get_singleton()->get("debugger/remote_scene_tree_refresh_interval");
- if (inspect_scene_tree->is_visible_in_tree()) {
- _scene_tree_request();
- }
- }
-
- 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 (inspected_object_id.is_valid()) {
- if (ScriptEditorDebuggerInspectedObject *obj = Object::cast_to<ScriptEditorDebuggerInspectedObject>(ObjectDB::get_instance(editor->get_editor_history()->get_current()))) {
- if (obj->remote_object_id == inspected_object_id) {
- //take the chance and re-inspect selected object
- Array msg;
- msg.push_back("inspect_object");
- msg.push_back(inspected_object_id);
- ppeer->put_var(msg);
- }
- }
- }
- }
+ if (is_session_active()) {
if (camera_override == OVERRIDE_2D) {
CanvasItemEditor *editor = CanvasItemEditor::get_singleton();
@@ -1277,9 +784,8 @@ void ScriptEditorDebugger::_notification(int p_what) {
transform.elements[2] = -offset * zoom;
Array msg;
- msg.push_back("override_camera_2D:transform");
msg.push_back(transform);
- ppeer->put_var(msg);
+ _put_msg("override_camera_2D:transform", msg);
} else if (camera_override >= OVERRIDE_3D_1) {
int viewport_idx = camera_override - OVERRIDE_3D_1;
@@ -1287,7 +793,6 @@ void ScriptEditorDebugger::_notification(int p_what) {
Camera *const cam = viewport->get_camera();
Array msg;
- msg.push_back("override_camera_3D:transform");
msg.push_back(cam->get_camera_transform());
if (cam->get_projection() == Camera::PROJECTION_ORTHOGONAL) {
msg.push_back(false);
@@ -1298,86 +803,12 @@ void ScriptEditorDebugger::_notification(int p_what) {
}
msg.push_back(cam->get_znear());
msg.push_back(cam->get_zfar());
- ppeer->put_var(msg);
- }
- }
-
- if (error_count != last_error_count || warning_count != last_warning_count) {
-
- if (error_count == 0 && warning_count == 0) {
- errors_tab->set_name(TTR("Errors"));
- debugger_button->set_text(TTR("Debugger"));
- debugger_button->set_icon(Ref<Texture2D>());
- tabs->set_tab_icon(errors_tab->get_index(), Ref<Texture2D>());
- } else {
- errors_tab->set_name(TTR("Errors") + " (" + itos(error_count + warning_count) + ")");
- debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")");
- if (error_count == 0) {
- debugger_button->set_icon(get_icon("Warning", "EditorIcons"));
- tabs->set_tab_icon(errors_tab->get_index(), get_icon("Warning", "EditorIcons"));
- } else {
- debugger_button->set_icon(get_icon("Error", "EditorIcons"));
- tabs->set_tab_icon(errors_tab->get_index(), get_icon("Error", "EditorIcons"));
- }
- }
- last_error_count = error_count;
- last_warning_count = warning_count;
- }
-
- if (server->is_connection_available()) {
- if (connection.is_valid()) {
- // We already have a valid connection. Disconnecting any new connecting client to prevent it from hanging.
- // (If we don't keep a reference to the connection it will be destroyed and disconnect_from_host will be called internally)
- server->take_connection();
- } else {
- // We just got the first connection.
- connection = server->take_connection();
- if (connection.is_null())
- break;
-
- EditorNode::get_log()->add_message("--- Debugging process started ---", EditorLog::MSG_TYPE_EDITOR);
-
- ppeer->set_stream_peer(connection);
-
- //EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
- //emit_signal("show_debugger",true);
-
- dobreak->set_disabled(false);
- tabs->set_current_tab(0);
-
- _set_reason_text(TTR("Child process connected."), MESSAGE_SUCCESS);
- profiler->clear();
-
- inspect_scene_tree->clear();
- le_set->set_disabled(true);
- le_clear->set_disabled(false);
- vmem_refresh->set_disabled(false);
- error_tree->clear();
- error_count = 0;
- warning_count = 0;
- profiler_signature.clear();
- //live_edit_root->set_text("/root");
-
- EditorNode::get_singleton()->get_pause_button()->set_pressed(false);
- EditorNode::get_singleton()->get_pause_button()->set_disabled(false);
-
- update_live_edit_root();
- if (profiler->is_profiling()) {
- _profiler_activate(true);
- }
-
- if (network_profiler->is_profiling()) {
- _network_profiler_activate(true);
- }
+ _put_msg("override_camera_3D:transform", msg);
}
}
- if (connection.is_null())
- break;
-
- if (!connection->is_connected_to_host()) {
- stop();
- editor->notify_child_process_exited(); //somehow, exited
+ if (!is_session_active()) {
+ _stop_and_notify();
break;
};
@@ -1389,67 +820,22 @@ void ScriptEditorDebugger::_notification(int p_what) {
while (ppeer->get_available_packet_count() > 0) {
- if (pending_in_queue) {
-
- int todo = MIN(ppeer->get_available_packet_count(), pending_in_queue);
-
- for (int i = 0; i < todo; i++) {
-
- Variant cmd;
- Error ret = ppeer->get_var(cmd);
- if (ret != OK) {
- stop();
- ERR_FAIL_COND(ret != OK);
- }
-
- message.push_back(cmd);
- pending_in_queue--;
- }
-
- if (pending_in_queue == 0) {
- _parse_message(message_type, message);
- message.clear();
- }
-
- } else {
-
- if (ppeer->get_available_packet_count() >= 2) {
-
- Variant cmd;
- Error ret = ppeer->get_var(cmd);
- if (ret != OK) {
- stop();
- ERR_FAIL_COND(ret != OK);
- }
- if (cmd.get_type() != Variant::STRING) {
- stop();
- ERR_FAIL_COND(cmd.get_type() != Variant::STRING);
- }
-
- message_type = cmd;
-
- ret = ppeer->get_var(cmd);
- if (ret != OK) {
- stop();
- ERR_FAIL_COND(ret != OK);
- }
- if (cmd.get_type() != Variant::INT) {
- stop();
- ERR_FAIL_COND(cmd.get_type() != Variant::INT);
- }
-
- pending_in_queue = cmd;
-
- if (pending_in_queue == 0) {
- _parse_message(message_type, Array());
- message.clear();
- }
-
- } else {
-
- break;
- }
+ Variant cmd;
+ Error ret = ppeer->get_var(cmd);
+ if (ret != OK) {
+ _stop_and_notify();
+ ERR_FAIL_MSG("Error decoding variant from peer");
+ }
+ if (cmd.get_type() != Variant::ARRAY) {
+ _stop_and_notify();
+ ERR_FAIL_MSG("Invalid message format received from peer");
}
+ Array arr = cmd;
+ if (arr.size() != 2 || arr[0].get_type() != Variant::STRING || arr[1].get_type() != Variant::ARRAY) {
+ _stop_and_notify();
+ ERR_FAIL_MSG("Invalid message format received from peer");
+ }
+ _parse_message(arr[0], arr[1]);
if (OS::get_singleton()->get_ticks_msec() > until)
break;
@@ -1460,15 +846,13 @@ void ScriptEditorDebugger::_notification(int p_what) {
add_constant_override("margin_left", -EditorNode::get_singleton()->get_gui_base()->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(MARGIN_LEFT));
add_constant_override("margin_right", -EditorNode::get_singleton()->get_gui_base()->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(MARGIN_RIGHT));
- tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles"));
- tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles"));
- tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles"));
+ tabs->add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles"));
+ tabs->add_style_override("tab_fg", EditorNode::get_singleton()->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles"));
+ tabs->add_style_override("tab_bg", EditorNode::get_singleton()->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles"));
copy->set_icon(get_icon("ActionCopy", "EditorIcons"));
step->set_icon(get_icon("DebugStep", "EditorIcons"));
next->set_icon(get_icon("DebugNext", "EditorIcons"));
- back->set_icon(get_icon("Back", "EditorIcons"));
- forward->set_icon(get_icon("Forward", "EditorIcons"));
dobreak->set_icon(get_icon("Pause", "EditorIcons"));
docontinue->set_icon(get_icon("DebugContinue", "EditorIcons"));
vmem_refresh->set_icon(get_icon("Reload", "EditorIcons"));
@@ -1486,15 +870,18 @@ void ScriptEditorDebugger::_clear_execution() {
stack_script = ResourceLoader::load(d["file"]);
emit_signal("clear_execution", stack_script);
stack_script.unref();
+ stack_dump->clear();
+ inspector->clear_stack_variables();
}
-void ScriptEditorDebugger::start() {
+void ScriptEditorDebugger::start(Ref<StreamPeerTCP> p_connection) {
+ error_count = 0;
+ warning_count = 0;
stop();
- if (is_visible_in_tree()) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
- }
+ connection = p_connection;
+ ppeer->set_stream_peer(connection);
perf_history.clear();
for (int i = 0; i < Performance::MONITOR_MAX; i++) {
@@ -1502,91 +889,81 @@ void ScriptEditorDebugger::start() {
perf_max.write[i] = 0;
}
- int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
- if (server->listen(remote_port) != OK) {
- EditorNode::get_log()->add_message(String("Error listening on port ") + itos(remote_port), EditorLog::MSG_TYPE_ERROR);
- return;
- }
-
- EditorNode::get_singleton()->get_scene_tree_dock()->show_tab_buttons();
- 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();
- }
-
set_process(true);
breaked = false;
+ can_debug = true;
camera_override = OVERRIDE_NONE;
+
+ tabs->set_current_tab(0);
+ _set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS);
+ _update_buttons_state();
+
+ if (profiler->is_profiling()) {
+ _profiler_activate(true);
+ }
+
+ if (network_profiler->is_profiling()) {
+ _network_profiler_activate(true);
+ }
}
-void ScriptEditorDebugger::pause() {
+void ScriptEditorDebugger::_update_buttons_state() {
+ const bool active = is_session_active();
+ const bool has_editor_tree = active && editor_remote_tree && editor_remote_tree->get_selected();
+ vmem_refresh->set_disabled(!active);
+ step->set_disabled(!active || !breaked || !can_debug);
+ next->set_disabled(!active || !breaked || !can_debug);
+ copy->set_disabled(!active || !breaked);
+ docontinue->set_disabled(!active || !breaked);
+ dobreak->set_disabled(!active || breaked);
+ le_clear->set_disabled(!active);
+ le_set->set_disabled(!has_editor_tree);
}
-void ScriptEditorDebugger::unpause() {
+void ScriptEditorDebugger::_stop_and_notify() {
+ stop();
+ emit_signal("stopped");
+ _set_reason_text(TTR("Debug session closed."), MESSAGE_WARNING);
}
void ScriptEditorDebugger::stop() {
set_process(false);
breaked = false;
+ can_debug = false;
+ remote_pid = 0;
_clear_execution();
- server->stop();
- _clear_remote_objects();
+ inspector->clear_cache();
ppeer->set_stream_peer(Ref<StreamPeer>());
if (connection.is_valid()) {
- EditorNode::get_log()->add_message("--- Debugging process stopped ---", EditorLog::MSG_TYPE_EDITOR);
connection.unref();
-
reason->set_text("");
reason->set_tooltip("");
}
- pending_in_queue = 0;
- message.clear();
-
node_path_cache.clear();
res_path_cache.clear();
profiler_signature.clear();
- le_clear->set_disabled(false);
- le_set->set_disabled(true);
- profiler->set_enabled(true);
- vmem_refresh->set_disabled(true);
- inspect_scene_tree->clear();
inspector->edit(NULL);
- 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();
-
- if (hide_on_stop) {
- if (is_visible_in_tree())
- EditorNode::get_singleton()->hide_bottom_panel();
- emit_signal("show_debugger", false);
- }
+ _update_buttons_state();
}
void ScriptEditorDebugger::_profiler_activate(bool p_enable) {
- if (!connection.is_valid())
- return;
-
if (p_enable) {
profiler_signature.clear();
Array msg;
- msg.push_back("start_profiling");
int max_funcs = EditorSettings::get_singleton()->get("debugger/profiler_frame_max_functions");
max_funcs = CLAMP(max_funcs, 16, 512);
msg.push_back(max_funcs);
- ppeer->put_var(msg);
+ _put_msg("start_profiling", msg);
print_verbose("Starting profiling.");
} else {
- Array msg;
- msg.push_back("stop_profiling");
- ppeer->put_var(msg);
+ _put_msg("stop_profiling", Array());
print_verbose("Ending profiling.");
}
}
@@ -1598,44 +975,30 @@ void ScriptEditorDebugger::_visual_profiler_activate(bool p_enable) {
if (p_enable) {
profiler_signature.clear();
- Array msg;
- msg.push_back("start_visual_profiling");
- ppeer->put_var(msg);
+ _put_msg("start_visual_profiling", Array());
print_verbose("Starting visual profiling.");
} else {
- Array msg;
- msg.push_back("stop_visual_profiling");
- ppeer->put_var(msg);
+ _put_msg("stop_visual_profiling", Array());
print_verbose("Ending visual profiling.");
}
}
void ScriptEditorDebugger::_network_profiler_activate(bool p_enable) {
- if (!connection.is_valid())
- return;
-
if (p_enable) {
profiler_signature.clear();
- Array msg;
- msg.push_back("start_network_profiling");
- ppeer->put_var(msg);
+ _put_msg("start_network_profiling", Array());
print_verbose("Starting network profiling.");
} else {
- Array msg;
- msg.push_back("stop_network_profiling");
- ppeer->put_var(msg);
+ _put_msg("stop_network_profiling", Array());
print_verbose("Ending network profiling.");
}
}
void ScriptEditorDebugger::_profiler_seeked() {
- if (!connection.is_valid() || !connection->is_connected_to_host())
- return;
-
if (breaked)
return;
debug_break();
@@ -1643,45 +1006,30 @@ void ScriptEditorDebugger::_profiler_seeked() {
void ScriptEditorDebugger::_stack_dump_frame_selected() {
- TreeItem *ti = stack_dump->get_selected();
- if (!ti)
- return;
+ emit_signal("stack_frame_selected");
- Dictionary d = ti->get_metadata(0);
-
- stack_script = ResourceLoader::load(d["file"]);
- emit_signal("goto_script_line", stack_script, int(d["line"]) - 1);
- emit_signal("set_execution", stack_script, int(d["line"]) - 1);
- stack_script.unref();
+ int frame = get_stack_script_frame();
- if (connection.is_valid() && connection->is_connected_to_host()) {
+ if (is_session_active() && frame >= 0) {
Array msg;
- msg.push_back("get_stack_frame_vars");
- msg.push_back(d["frame"]);
- ppeer->put_var(msg);
+ msg.push_back(frame);
+ _put_msg("get_stack_frame_vars", msg);
} else {
inspector->edit(NULL);
}
}
-void ScriptEditorDebugger::_output_clear() {
-
- //output->clear();
- //output->push_color(Color(0,0,0));
-}
-
void ScriptEditorDebugger::_export_csv() {
file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
- file_dialog_mode = SAVE_CSV;
file_dialog->popup_centered_ratio();
}
String ScriptEditorDebugger::get_var_value(const String &p_var) const {
if (!breaked)
return String();
- return variables->get_var_value(p_var);
+ return inspector->get_stack_variable(p_var);
}
int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) {
@@ -1694,10 +1042,9 @@ int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) {
node_path_cache[p_path] = last_path_id;
Array msg;
- msg.push_back("live_node_path");
msg.push_back(p_path);
msg.push_back(last_path_id);
- ppeer->put_var(msg);
+ _put_msg("live_node_path", msg);
return last_path_id;
}
@@ -1713,17 +1060,16 @@ int ScriptEditorDebugger::_get_res_path_cache(const String &p_path) {
res_path_cache[p_path] = last_path_id;
Array msg;
- msg.push_back("live_res_path");
msg.push_back(p_path);
msg.push_back(last_path_id);
- ppeer->put_var(msg);
+ _put_msg("live_res_path", msg);
return last_path_id;
}
void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE) {
- if (!p_base || !live_debug || !connection.is_valid() || !editor->get_edited_scene())
+ if (!p_base || !live_debug || !is_session_active() || !editor->get_edited_scene())
return;
Node *node = Object::cast_to<Node>(p_base);
@@ -1742,14 +1088,13 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n
int pathid = _get_node_path_cache(path);
Array msg;
- msg.push_back("live_node_call");
msg.push_back(pathid);
msg.push_back(p_name);
for (int i = 0; i < VARIANT_ARG_MAX; i++) {
//no pointers, sorry
msg.push_back(*argptr[i]);
}
- ppeer->put_var(msg);
+ _put_msg("live_node_call", msg);
return;
}
@@ -1762,14 +1107,13 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n
int pathid = _get_res_path_cache(respath);
Array msg;
- msg.push_back("live_res_call");
msg.push_back(pathid);
msg.push_back(p_name);
for (int i = 0; i < VARIANT_ARG_MAX; i++) {
//no pointers, sorry
msg.push_back(*argptr[i]);
}
- ppeer->put_var(msg);
+ _put_msg("live_res_call", msg);
return;
}
@@ -1777,7 +1121,7 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n
void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p_property, const Variant &p_value) {
- if (!p_base || !live_debug || !connection.is_valid() || !editor->get_edited_scene())
+ if (!p_base || !live_debug || !editor->get_edited_scene())
return;
Node *node = Object::cast_to<Node>(p_base);
@@ -1792,20 +1136,18 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p
if (res.is_valid() && res->get_path() != String()) {
Array msg;
- msg.push_back("live_node_prop_res");
msg.push_back(pathid);
msg.push_back(p_property);
msg.push_back(res->get_path());
- ppeer->put_var(msg);
+ _put_msg("live_node_prop_res", msg);
}
} else {
Array msg;
- msg.push_back("live_node_prop");
msg.push_back(pathid);
msg.push_back(p_property);
msg.push_back(p_value);
- ppeer->put_var(msg);
+ _put_msg("live_node_prop", msg);
}
return;
@@ -1823,36 +1165,46 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p
if (res2.is_valid() && res2->get_path() != String()) {
Array msg;
- msg.push_back("live_res_prop_res");
msg.push_back(pathid);
msg.push_back(p_property);
msg.push_back(res2->get_path());
- ppeer->put_var(msg);
+ _put_msg("live_res_prop_res", msg);
}
} else {
Array msg;
- msg.push_back("live_res_prop");
msg.push_back(pathid);
msg.push_back(p_property);
msg.push_back(p_value);
- ppeer->put_var(msg);
+ _put_msg("live_res_prop", msg);
}
return;
}
}
-void ScriptEditorDebugger::_method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE) {
-
- ScriptEditorDebugger *sed = (ScriptEditorDebugger *)p_ud;
- sed->_method_changed(p_base, p_name, VARIANT_ARG_PASS);
+String ScriptEditorDebugger::get_stack_script_file() const {
+ TreeItem *ti = stack_dump->get_selected();
+ if (!ti)
+ return "";
+ Dictionary d = ti->get_metadata(0);
+ return d["file"];
}
-void ScriptEditorDebugger::_property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value) {
+int ScriptEditorDebugger::get_stack_script_line() const {
+ TreeItem *ti = stack_dump->get_selected();
+ if (!ti)
+ return -1;
+ Dictionary d = ti->get_metadata(0);
+ return d["line"];
+}
- ScriptEditorDebugger *sed = (ScriptEditorDebugger *)p_ud;
- sed->_property_changed(p_base, p_property, p_value);
+int ScriptEditorDebugger::get_stack_script_frame() const {
+ TreeItem *ti = stack_dump->get_selected();
+ if (!ti)
+ return -1;
+ Dictionary d = ti->get_metadata(0);
+ return d["frame"];
}
void ScriptEditorDebugger::set_live_debugging(bool p_enable) {
@@ -1862,12 +1214,13 @@ void ScriptEditorDebugger::set_live_debugging(bool p_enable) {
void ScriptEditorDebugger::_live_edit_set() {
- if (!connection.is_valid())
+ if (!is_session_active() || !editor_remote_tree)
return;
- TreeItem *ti = inspect_scene_tree->get_selected();
+ TreeItem *ti = editor_remote_tree->get_selected();
if (!ti)
return;
+
String path;
while (ti) {
@@ -1895,92 +1248,82 @@ void ScriptEditorDebugger::update_live_edit_root() {
NodePath np = editor->get_editor_data().get_edited_scene_live_edit_root();
- if (connection.is_valid()) {
- Array msg;
- msg.push_back("live_set_root");
- msg.push_back(np);
- if (editor->get_edited_scene())
- msg.push_back(editor->get_edited_scene()->get_filename());
- else
- msg.push_back("");
- ppeer->put_var(msg);
- }
+ Array msg;
+ msg.push_back(np);
+ if (editor->get_edited_scene())
+ msg.push_back(editor->get_edited_scene()->get_filename());
+ else
+ msg.push_back("");
+ _put_msg("live_set_root", msg);
live_edit_root->set_text(np);
}
void ScriptEditorDebugger::live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name) {
- if (live_debug && connection.is_valid()) {
+ if (live_debug) {
Array msg;
- msg.push_back("live_create_node");
msg.push_back(p_parent);
msg.push_back(p_type);
msg.push_back(p_name);
- ppeer->put_var(msg);
+ _put_msg("live_create_node", msg);
}
}
void ScriptEditorDebugger::live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name) {
- if (live_debug && connection.is_valid()) {
+ if (live_debug) {
Array msg;
- msg.push_back("live_instance_node");
msg.push_back(p_parent);
msg.push_back(p_path);
msg.push_back(p_name);
- ppeer->put_var(msg);
+ _put_msg("live_instance_node", msg);
}
}
void ScriptEditorDebugger::live_debug_remove_node(const NodePath &p_at) {
- if (live_debug && connection.is_valid()) {
+ if (live_debug) {
Array msg;
- msg.push_back("live_remove_node");
msg.push_back(p_at);
- ppeer->put_var(msg);
+ _put_msg("live_remove_node", msg);
}
}
void ScriptEditorDebugger::live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id) {
- if (live_debug && connection.is_valid()) {
+ if (live_debug) {
Array msg;
- msg.push_back("live_remove_and_keep_node");
msg.push_back(p_at);
msg.push_back(p_keep_id);
- ppeer->put_var(msg);
+ _put_msg("live_remove_and_keep_node", msg);
}
}
void ScriptEditorDebugger::live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos) {
- if (live_debug && connection.is_valid()) {
+ if (live_debug) {
Array msg;
- msg.push_back("live_restore_node");
msg.push_back(p_id);
msg.push_back(p_at);
msg.push_back(p_at_pos);
- ppeer->put_var(msg);
+ _put_msg("live_restore_node", msg);
}
}
void ScriptEditorDebugger::live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name) {
- if (live_debug && connection.is_valid()) {
+ if (live_debug) {
Array msg;
- msg.push_back("live_duplicate_node");
msg.push_back(p_at);
msg.push_back(p_new_name);
- ppeer->put_var(msg);
+ _put_msg("live_duplicate_node", msg);
}
}
void ScriptEditorDebugger::live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {
- if (live_debug && connection.is_valid()) {
+ if (live_debug) {
Array msg;
- msg.push_back("live_reparent_node");
msg.push_back(p_at);
msg.push_back(p_new_place);
msg.push_back(p_new_name);
msg.push_back(p_at_pos);
- ppeer->put_var(msg);
+ _put_msg("live_reparent_node", msg);
}
}
@@ -1991,33 +1334,21 @@ ScriptEditorDebugger::CameraOverride ScriptEditorDebugger::get_camera_override()
void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) {
if (p_override == OVERRIDE_2D && camera_override != OVERRIDE_2D) {
- if (connection.is_valid()) {
- Array msg;
- msg.push_back("override_camera_2D:set");
- msg.push_back(true);
- ppeer->put_var(msg);
- }
+ Array msg;
+ msg.push_back(true);
+ _put_msg("override_camera_2D:set", msg);
} else if (p_override != OVERRIDE_2D && camera_override == OVERRIDE_2D) {
- if (connection.is_valid()) {
- Array msg;
- msg.push_back("override_camera_2D:set");
- msg.push_back(false);
- ppeer->put_var(msg);
- }
+ Array msg;
+ msg.push_back(false);
+ _put_msg("override_camera_2D:set", msg);
} else if (p_override >= OVERRIDE_3D_1 && camera_override < OVERRIDE_3D_1) {
- if (connection.is_valid()) {
- Array msg;
- msg.push_back("override_camera_3D:set");
- msg.push_back(true);
- ppeer->put_var(msg);
- }
+ Array msg;
+ msg.push_back(true);
+ _put_msg("override_camera_3D:set", msg);
} else if (p_override < OVERRIDE_3D_1 && camera_override >= OVERRIDE_3D_1) {
- if (connection.is_valid()) {
- Array msg;
- msg.push_back("override_camera_3D:set");
- msg.push_back(false);
- ppeer->put_var(msg);
- }
+ Array msg;
+ msg.push_back(false);
+ _put_msg("override_camera_3D:set", msg);
}
camera_override = p_override;
@@ -2025,23 +1356,16 @@ void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) {
void ScriptEditorDebugger::set_breakpoint(const String &p_path, int p_line, bool p_enabled) {
- if (connection.is_valid()) {
- Array msg;
- msg.push_back("breakpoint");
- msg.push_back(p_path);
- msg.push_back(p_line);
- msg.push_back(p_enabled);
- ppeer->put_var(msg);
- }
+ Array msg;
+ msg.push_back(p_path);
+ msg.push_back(p_line);
+ msg.push_back(p_enabled);
+ _put_msg("breakpoint", msg);
}
void ScriptEditorDebugger::reload_scripts() {
- if (connection.is_valid()) {
- Array msg;
- msg.push_back("reload_scripts");
- ppeer->put_var(msg);
- }
+ _put_msg("reload_scripts", Array());
}
bool ScriptEditorDebugger::is_skip_breakpoints() {
@@ -2059,15 +1383,12 @@ void ScriptEditorDebugger::_error_activated() {
void ScriptEditorDebugger::_error_selected() {
TreeItem *selected = error_tree->get_selected();
-
Array meta = selected->get_metadata(0);
-
if (meta.size() == 0) {
return;
}
- Ref<Script> s = ResourceLoader::load(meta[0]);
- emit_signal("goto_script_line", s, int(meta[1]) - 1);
+ emit_signal("error_selected", String(meta[0]), int(meta[1]));
}
void ScriptEditorDebugger::_expand_errors_list() {
@@ -2096,58 +1417,6 @@ void ScriptEditorDebugger::_collapse_errors_list() {
}
}
-void ScriptEditorDebugger::set_hide_on_stop(bool p_hide) {
-
- hide_on_stop = p_hide;
-}
-
-bool ScriptEditorDebugger::get_debug_with_external_editor() const {
-
- return enable_external_editor;
-}
-
-void ScriptEditorDebugger::set_debug_with_external_editor(bool p_enabled) {
-
- enable_external_editor = p_enabled;
-}
-
-Ref<Script> ScriptEditorDebugger::get_dump_stack_script() const {
-
- return stack_script;
-}
-
-void ScriptEditorDebugger::_paused() {
-
- ERR_FAIL_COND(connection.is_null());
- ERR_FAIL_COND(!connection->is_connected_to_host());
-
- if (!breaked && EditorNode::get_singleton()->get_pause_button()->is_pressed()) {
- debug_break();
- }
-
- if (breaked && !EditorNode::get_singleton()->get_pause_button()->is_pressed()) {
- debug_continue();
- }
-}
-
-void ScriptEditorDebugger::_set_remote_object(ObjectID p_id, ScriptEditorDebuggerInspectedObject *p_obj) {
-
- if (remote_objects.has(p_id))
- memdelete(remote_objects[p_id]);
- remote_objects[p_id] = p_obj;
-}
-
-void ScriptEditorDebugger::_clear_remote_objects() {
-
- for (Map<ObjectID, ScriptEditorDebuggerInspectedObject *>::Element *E = remote_objects.front(); E; E = E->next()) {
- if (editor->get_editor_history()->get_current() == E->value()->get_instance_id()) {
- editor->push_item(NULL);
- }
- memdelete(E->value());
- }
- remote_objects.clear();
-}
-
void ScriptEditorDebugger::_clear_errors_list() {
error_tree->clear();
@@ -2163,7 +1432,7 @@ void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos) {
item_menu->set_size(Size2(1, 1));
if (error_tree->is_anything_selected()) {
- item_menu->add_icon_item(get_icon("ActionCopy", "EditorIcons"), TTR("Copy Error"), ITEM_MENU_COPY_ERROR);
+ item_menu->add_icon_item(get_icon("ActionCopy", "EditorIcons"), TTR("Copy Error"), 0);
}
if (item_menu->get_item_count() > 0) {
@@ -2173,70 +1442,29 @@ void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos) {
}
void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) {
+ TreeItem *ti = error_tree->get_selected();
+ while (ti->get_parent() != error_tree->get_root())
+ ti = ti->get_parent();
- switch (p_option) {
-
- case ITEM_MENU_COPY_ERROR: {
- TreeItem *ti = error_tree->get_selected();
- while (ti->get_parent() != error_tree->get_root())
- ti = ti->get_parent();
-
- String type;
-
- if (ti->get_icon(0) == get_icon("Warning", "EditorIcons")) {
- type = "W ";
- } else if (ti->get_icon(0) == get_icon("Error", "EditorIcons")) {
- type = "E ";
- }
-
- String text = ti->get_text(0) + " ";
- int rpad_len = text.length();
-
- text = type + text + ti->get_text(1) + "\n";
- TreeItem *ci = ti->get_children();
- while (ci) {
- text += " " + ci->get_text(0).rpad(rpad_len) + ci->get_text(1) + "\n";
- ci = ci->get_next();
- }
-
- OS::get_singleton()->set_clipboard(text);
-
- } break;
- case ITEM_MENU_SAVE_REMOTE_NODE: {
-
- file_dialog->set_access(EditorFileDialog::ACCESS_RESOURCES);
- file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
- file_dialog_mode = SAVE_NODE;
-
- 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());
- }
+ String type;
- file_dialog->popup_centered_ratio();
- } break;
- case ITEM_MENU_COPY_NODE_PATH: {
+ if (ti->get_icon(0) == get_icon("Warning", "EditorIcons")) {
+ type = "W ";
+ } else if (ti->get_icon(0) == get_icon("Error", "EditorIcons")) {
+ type = "E ";
+ }
- TreeItem *ti = inspect_scene_tree->get_selected();
- String text = ti->get_text(0);
+ String text = ti->get_text(0) + " ";
+ int rpad_len = text.length();
- if (ti->get_parent() == NULL) {
- text = ".";
- } else if (ti->get_parent()->get_parent() == NULL) {
- text = ".";
- } else {
- while (ti->get_parent()->get_parent() != inspect_scene_tree->get_root()) {
- ti = ti->get_parent();
- text = ti->get_text(0) + "/" + text;
- }
- }
-
- OS::get_singleton()->set_clipboard(text);
- } break;
+ text = type + text + ti->get_text(1) + "\n";
+ TreeItem *ci = ti->get_children();
+ while (ci) {
+ text += " " + ci->get_text(0).rpad(rpad_len) + ci->get_text(1) + "\n";
+ ci = ci->get_next();
}
+
+ OS::get_singleton()->set_clipboard(text);
}
void ScriptEditorDebugger::_tab_changed(int p_tab) {
@@ -2257,11 +1485,9 @@ void ScriptEditorDebugger::_bind_methods() {
ClassDB::bind_method(D_METHOD("debug_step"), &ScriptEditorDebugger::debug_step);
ClassDB::bind_method(D_METHOD("debug_break"), &ScriptEditorDebugger::debug_break);
ClassDB::bind_method(D_METHOD("debug_continue"), &ScriptEditorDebugger::debug_continue);
- ClassDB::bind_method(D_METHOD("_output_clear"), &ScriptEditorDebugger::_output_clear);
ClassDB::bind_method(D_METHOD("_export_csv"), &ScriptEditorDebugger::_export_csv);
ClassDB::bind_method(D_METHOD("_performance_draw"), &ScriptEditorDebugger::_performance_draw);
ClassDB::bind_method(D_METHOD("_performance_select"), &ScriptEditorDebugger::_performance_select);
- ClassDB::bind_method(D_METHOD("_scene_tree_request"), &ScriptEditorDebugger::_scene_tree_request);
ClassDB::bind_method(D_METHOD("_video_mem_request"), &ScriptEditorDebugger::_video_mem_request);
ClassDB::bind_method(D_METHOD("_live_edit_set"), &ScriptEditorDebugger::_live_edit_set);
ClassDB::bind_method(D_METHOD("_live_edit_clear"), &ScriptEditorDebugger::_live_edit_clear);
@@ -2279,13 +1505,10 @@ void ScriptEditorDebugger::_bind_methods() {
ClassDB::bind_method(D_METHOD("_error_tree_item_rmb_selected"), &ScriptEditorDebugger::_error_tree_item_rmb_selected);
ClassDB::bind_method(D_METHOD("_item_menu_id_pressed"), &ScriptEditorDebugger::_item_menu_id_pressed);
ClassDB::bind_method(D_METHOD("_tab_changed"), &ScriptEditorDebugger::_tab_changed);
-
- ClassDB::bind_method(D_METHOD("_paused"), &ScriptEditorDebugger::_paused);
-
- ClassDB::bind_method(D_METHOD("_scene_tree_selected"), &ScriptEditorDebugger::_scene_tree_selected);
- ClassDB::bind_method(D_METHOD("_scene_tree_folded"), &ScriptEditorDebugger::_scene_tree_folded);
- ClassDB::bind_method(D_METHOD("_scene_tree_rmb_selected"), &ScriptEditorDebugger::_scene_tree_rmb_selected);
ClassDB::bind_method(D_METHOD("_file_selected"), &ScriptEditorDebugger::_file_selected);
+ ClassDB::bind_method(D_METHOD("_remote_object_selected", "id"), &ScriptEditorDebugger::_remote_object_selected);
+ ClassDB::bind_method(D_METHOD("_remote_object_edited", "id", "property", "value"), &ScriptEditorDebugger::_remote_object_edited);
+ ClassDB::bind_method(D_METHOD("_remote_object_property_updated", "id", "property"), &ScriptEditorDebugger::_remote_object_property_updated);
ClassDB::bind_method(D_METHOD("live_debug_create_node"), &ScriptEditorDebugger::live_debug_create_node);
ClassDB::bind_method(D_METHOD("live_debug_instance_node"), &ScriptEditorDebugger::live_debug_instance_node);
@@ -2294,14 +1517,20 @@ void ScriptEditorDebugger::_bind_methods() {
ClassDB::bind_method(D_METHOD("live_debug_restore_node"), &ScriptEditorDebugger::live_debug_restore_node);
ClassDB::bind_method(D_METHOD("live_debug_duplicate_node"), &ScriptEditorDebugger::live_debug_duplicate_node);
ClassDB::bind_method(D_METHOD("live_debug_reparent_node"), &ScriptEditorDebugger::live_debug_reparent_node);
- ClassDB::bind_method(D_METHOD("_scene_tree_property_select_object"), &ScriptEditorDebugger::_scene_tree_property_select_object);
- ClassDB::bind_method(D_METHOD("_scene_tree_property_value_edited"), &ScriptEditorDebugger::_scene_tree_property_value_edited);
+ ClassDB::bind_method(D_METHOD("request_remote_object", "id"), &ScriptEditorDebugger::request_remote_object);
+ ClassDB::bind_method(D_METHOD("update_remote_object", "id", "property", "value"), &ScriptEditorDebugger::update_remote_object);
- ADD_SIGNAL(MethodInfo("goto_script_line"));
+ ADD_SIGNAL(MethodInfo("stopped"));
+ ADD_SIGNAL(MethodInfo("stop_requested"));
+ ADD_SIGNAL(MethodInfo("stack_frame_selected", PropertyInfo(Variant::INT, "frame")));
+ ADD_SIGNAL(MethodInfo("error_selected", PropertyInfo(Variant::INT, "error")));
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")));
- ADD_SIGNAL(MethodInfo("show_debugger", PropertyInfo(Variant::BOOL, "reallydid")));
+ ADD_SIGNAL(MethodInfo("remote_object_requested", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("remote_object_updated", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("remote_object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property")));
+ ADD_SIGNAL(MethodInfo("remote_tree_updated"));
}
ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
@@ -2312,7 +1541,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
ppeer = Ref<PacketPeerStream>(memnew(PacketPeerStream));
ppeer->set_input_buffer_max_size((1024 * 1024 * 8) - 4); // 8 MiB should be enough, minus 4 bytes for separator.
editor = p_editor;
- editor->get_inspector()->connect_compat("object_id_selected", this, "_scene_tree_property_select_object");
tabs = memnew(TabContainer);
tabs->set_tab_align(TabContainer::ALIGN_LEFT);
@@ -2381,16 +1609,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
docontinue->set_shortcut(ED_GET_SHORTCUT("debugger/continue"));
docontinue->connect_compat("pressed", this, "debug_continue");
- back = memnew(Button);
- hbc->add_child(back);
- back->set_tooltip(TTR("Inspect Previous Instance"));
- back->hide();
-
- forward = memnew(Button);
- hbc->add_child(forward);
- forward->set_tooltip(TTR("Inspect Next Instance"));
- forward->hide();
-
HSplitContainer *sc = memnew(HSplitContainer);
vbc->add_child(sc);
sc->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -2405,21 +1623,14 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
stack_dump->connect_compat("cell_selected", this, "_stack_dump_frame_selected");
sc->add_child(stack_dump);
- inspector = memnew(EditorInspector);
+ inspector = memnew(EditorDebuggerInspector);
inspector->set_h_size_flags(SIZE_EXPAND_FILL);
inspector->set_enable_capitalize_paths(false);
inspector->set_read_only(true);
- inspector->connect_compat("object_id_selected", this, "_scene_tree_property_select_object");
+ inspector->connect_compat("object_selected", this, "_remote_object_selected");
+ inspector->connect_compat("object_edited", this, "_remote_object_edited");
+ inspector->connect_compat("object_property_updated", this, "_remote_object_property_updated");
sc->add_child(inspector);
-
- server.instance();
-
- pending_in_queue = 0;
-
- variables = memnew(ScriptEditorDebuggerVariables);
-
- breaked = false;
-
tabs->add_child(dbg);
}
@@ -2472,23 +1683,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
tabs->add_child(errors_tab);
}
- { // remote scene tree
-
- inspect_scene_tree = memnew(Tree);
- EditorNode::get_singleton()->get_scene_tree_dock()->add_remote_tree_editor(inspect_scene_tree);
- EditorNode::get_singleton()->get_scene_tree_dock()->connect_compat("remote_tree_selected", this, "_scene_tree_selected");
- inspect_scene_tree->set_v_size_flags(SIZE_EXPAND_FILL);
- inspect_scene_tree->connect_compat("cell_selected", this, "_scene_tree_selected");
- inspect_scene_tree->connect_compat("item_collapsed", this, "_scene_tree_folded");
- inspect_scene_tree->set_allow_rmb_select(true);
- inspect_scene_tree->connect_compat("item_rmb_selected", this, "_scene_tree_rmb_selected");
- auto_switch_remote_scene_tree = EDITOR_DEF("debugger/auto_switch_to_remote_scene_tree", false);
- inspect_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);
- inspected_object_id = ObjectID();
- updating_scene_tree = false;
- }
-
{ // File dialog
file_dialog = memnew(EditorFileDialog);
file_dialog->connect_compat("file_selected", this, "_file_selected");
@@ -2508,6 +1702,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
visual_profiler->set_name(TTR("Visual Profiler"));
tabs->add_child(visual_profiler);
visual_profiler->connect_compat("enable_profiling", this, "_visual_profiler_activate");
+ visual_profiler->connect_compat("break_request", this, "_profiler_seeked");
}
{ //network profiler
@@ -2590,7 +1785,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
vmem_total->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
vmem_hb->add_child(vmem_total);
vmem_refresh = memnew(ToolButton);
- vmem_refresh->set_disabled(true);
vmem_hb->add_child(vmem_refresh);
vmem_vb->add_child(vmem_hb);
vmem_refresh->connect_compat("pressed", this, "_video_mem_request");
@@ -2638,6 +1832,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
info_left->add_child(memnew(Label(TTR("Clicked Control Type:"))));
info_left->add_child(clicked_ctrl_type);
+ scene_tree = memnew(SceneDebuggerTree);
live_edit_root = memnew(LineEdit);
live_edit_root->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -2651,8 +1846,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
le_clear = memnew(Button(TTR("Clear")));
lehb->add_child(le_clear);
info_left->add_child(lehb);
- le_set->set_disabled(true);
- le_clear->set_disabled(true);
}
misc->add_child(memnew(VSeparator));
@@ -2669,27 +1862,18 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
msgdialog = memnew(AcceptDialog);
add_child(msgdialog);
- p_editor->get_undo_redo()->set_method_notify_callback(_method_changeds, this);
- p_editor->get_undo_redo()->set_property_notify_callback(_property_changeds, this);
live_debug = true;
camera_override = OVERRIDE_NONE;
last_path_id = false;
error_count = 0;
warning_count = 0;
- hide_on_stop = true;
- enable_external_editor = false;
- last_error_count = 0;
- last_warning_count = 0;
-
- EditorNode::get_singleton()->get_pause_button()->connect_compat("pressed", this, "_paused");
+ _update_buttons_state();
}
ScriptEditorDebugger::~ScriptEditorDebugger() {
- memdelete(variables);
-
ppeer->set_stream_peer(Ref<StreamPeer>());
- server->stop();
- _clear_remote_objects();
+ inspector->clear_cache();
+ memdelete(scene_tree);
}
diff --git a/editor/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h
index 5a821a4a88..ce205e95b1 100644
--- a/editor/script_editor_debugger.h
+++ b/editor/debugger/script_editor_debugger.h
@@ -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();
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 59fa40846f..413959bb99 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -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)));
diff --git a/editor/editor_node.h b/editor/editor_node.h
index ef9be0677d..a982b9d85f 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -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;
diff --git a/editor/editor_path.cpp b/editor/editor_path.cpp
index cfb70a087a..696474d4b1 100644
--- a/editor/editor_path.cpp
+++ b/editor/editor_path.cpp
@@ -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();
diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp
index ff7420e19b..3200a0ac8b 100644
--- a/editor/editor_run.cpp
+++ b/editor/editor_run.cpp
@@ -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;
diff --git a/editor/editor_run.h b/editor/editor_run.h
index b50a2c2f0e..389a1e6b20 100644
--- a/editor/editor_run.h
+++ b/editor/editor_run.h
@@ -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;
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index 6eaf6eb04a..4effdc63d1 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -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();
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index b91b346089..dfcaf5cdd2 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -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) {
diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp
new file mode 100644
index 0000000000..2534a2cc17
--- /dev/null
+++ b/editor/plugins/debugger_editor_plugin.cpp
@@ -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?
+}
diff --git a/editor/plugins/debugger_editor_plugin.h b/editor/plugins/debugger_editor_plugin.h
new file mode 100644
index 0000000000..05d6ece72d
--- /dev/null
+++ b/editor/plugins/debugger_editor_plugin.h
@@ -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
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index cea18b63f1..c0928ba79b 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -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->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);
+ debug_menu->hide(); // Handled by EditorDebuggerNode below.
+
+ 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");
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index ec84bcb461..b4b4f33fc5 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -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);
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 2069c035fb..4986e60ff0 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -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: {
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index 45f2a5063c..6e8eeaec82 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -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);
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index beba7f0ed6..ebc7d56b24 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -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 {
diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp
index c1a902bdcc..9f8a531762 100644
--- a/editor/settings_config_dialog.cpp
+++ b/editor/settings_config_dialog.cpp
@@ -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: {