From b2a4908e9cc802a838a67f92dd66a17d3894c619 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Sun, 22 May 2016 19:28:37 -0300 Subject: [PATCH] Real-Time Remote Inspector support --- core/object.cpp | 2 +- core/object.h | 1 + core/script_debugger_remote.cpp | 99 ++++ core/script_debugger_remote.h | 3 + scene/main/scene_main_loop.cpp | 1 + tools/editor/property_editor.cpp | 32 ++ tools/editor/script_editor_debugger.cpp | 707 +++++++++++++++++------- tools/editor/script_editor_debugger.h | 28 +- 8 files changed, 654 insertions(+), 219 deletions(-) diff --git a/core/object.cpp b/core/object.cpp index 7f4db7c8b54..3cfc0329bc7 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -1396,7 +1396,7 @@ Error Object::connect(const StringName& p_signal, Object *p_to_object, const Str signal_is_valid=true; if (!signal_is_valid) { - ERR_EXPLAIN("Attempt to connect nonexistent signal '"+p_signal+"' to method '"+p_to_method+"'"); + ERR_EXPLAIN("In Object of type '"+String(get_type())+"': Attempt to connect nonexistent signal '"+p_signal+"' to method '"+p_to_object->get_type()+"."+p_to_method+"'"); ERR_FAIL_COND_V(!signal_is_valid,ERR_INVALID_PARAMETER); } signal_map[p_signal]=Signal(); diff --git a/core/object.h b/core/object.h index c34440100f4..3945d1d0ba4 100644 --- a/core/object.h +++ b/core/object.h @@ -67,6 +67,7 @@ enum PropertyHint { PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color PROPERTY_HINT_IMAGE_COMPRESS_LOSSY, PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS, + PROPERTY_HINT_OBJECT_ID, PROPERTY_HINT_MAX, }; diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index 10e1b65cf36..011b1f78a07 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -283,6 +283,14 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script,bool p_can_continue) { } else if (command=="request_video_mem") { _send_video_memory(); + } else if (command=="inspect_object") { + + ObjectID id = cmd[1]; + _send_object_id(id); + } else if (command=="set_object_property") { + + _set_object_property(cmd[1],cmd[2],cmd[3]); + } else if (command=="breakpoint") { bool set = cmd[3]; @@ -539,6 +547,89 @@ bool ScriptDebuggerRemote::_parse_live_edit(const Array& cmd) { return true; } + +void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) { + + Object* obj = ObjectDB::get_instance(p_id); + if (!obj) + return; + + List pinfo; + obj->get_property_list(&pinfo,true); + + int props_to_send=0; + for (List::Element *E=pinfo.front();E;E=E->next()) { + + if (E->get().usage&(PROPERTY_USAGE_EDITOR|PROPERTY_USAGE_CATEGORY)) { + props_to_send++; + } + } + + packet_peer_stream->put_var("message:inspect_object"); + packet_peer_stream->put_var(props_to_send*5+4); + packet_peer_stream->put_var(p_id); + packet_peer_stream->put_var(obj->get_type()); + if (obj->is_type("Resource") || obj->is_type("Node")) + packet_peer_stream->put_var(obj->call("get_path")); + else + packet_peer_stream->put_var(""); + + packet_peer_stream->put_var(props_to_send); + + for (List::Element *E=pinfo.front();E;E=E->next()) { + + if (E->get().usage&(PROPERTY_USAGE_EDITOR|PROPERTY_USAGE_CATEGORY)) { + + if (E->get().usage&PROPERTY_USAGE_CATEGORY) { + packet_peer_stream->put_var("*"+E->get().name); + } else { + packet_peer_stream->put_var(E->get().name); + } + + Variant var = obj->get(E->get().name); + + if (E->get().type==Variant::OBJECT || var.get_type()==Variant::OBJECT) { + + ObjectID id2; + Object *obj=var; + if (obj) { + id2=obj->get_instance_ID(); + } else { + id2=0; + } + + packet_peer_stream->put_var(Variant::INT); //hint string + packet_peer_stream->put_var(PROPERTY_HINT_OBJECT_ID); //hint + packet_peer_stream->put_var(E->get().hint_string); //hint string + packet_peer_stream->put_var(id2); //value + } else { + packet_peer_stream->put_var(E->get().type); + packet_peer_stream->put_var(E->get().hint); + packet_peer_stream->put_var(E->get().hint_string); + //only send information that can be sent.. + if (var.get_type()==Variant::IMAGE) { + var=Image(); + } + if (var.get_type()>=Variant::DICTIONARY) { + var=Array(); //send none for now, may be to big + } + packet_peer_stream->put_var(var); + } + + } + } + +} + +void ScriptDebuggerRemote::_set_object_property(ObjectID p_id, const String& p_property, const Variant& p_value) { + + Object* obj = ObjectDB::get_instance(p_id); + if (!obj) + return; + + obj->set(p_property,p_value); +} + void ScriptDebuggerRemote::_poll_events() { //this si called from ::idle_poll, happens only when running the game, @@ -575,6 +666,14 @@ void ScriptDebuggerRemote::_poll_events() { } else if (command=="request_video_mem") { _send_video_memory(); + } else if (command=="inspect_object") { + + ObjectID id = cmd[1]; + _send_object_id(id); + } else if (command=="set_object_property") { + + _set_object_property(cmd[1],cmd[2],cmd[3]); + } else if (command=="start_profiling") { for(int i=0;iget_name()); array.push_back(p_node->get_type()); + array.push_back(p_node->get_instance_ID()); for(int i=0;iget_child_count();i++) { _fill_array(p_node->get_child(i),array,p_level+1); diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp index f94ca48ec2e..afc6396ab32 100644 --- a/tools/editor/property_editor.cpp +++ b/tools/editor/property_editor.cpp @@ -2989,6 +2989,32 @@ void PropertyEditor::update_tree() { item->set_range(1, obj->get( p.name ) ); item->set_editable(1,!read_only); break; + } else if (p.hint==PROPERTY_HINT_OBJECT_ID) { + +// int c = p.hint_string.get_slice_count(","); + item->set_cell_mode(1,TreeItem::CELL_MODE_CUSTOM); + + String type=p.hint_string; + if (type=="") + type="Object"; + + ObjectID id = obj->get( p.name ); + if (id!=0) { + item->set_text(1,type+" ID: "+itos(id)); + item->add_button(1,get_icon("EditResource","EditorIcons")); + } else { + item->set_text(1,"[Empty]"); + } + + if (has_icon(p.hint_string,"EditorIcons")) { + type=p.hint_string; + } else { + type="Object"; + } + + item->set_icon(0,get_icon(type,"EditorIcons")); + + break; } else { if (p.type == Variant::REAL) { @@ -3758,6 +3784,11 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) { emit_signal("resource_selected",r,n); } + } else if (t==Variant::INT && h==PROPERTY_HINT_OBJECT_ID) { + + emit_signal("object_id_selected",obj->get(n)); + print_line("OBJ ID SELECTED"); + } else if (t==Variant::ARRAY || t==Variant::INT_ARRAY || t==Variant::REAL_ARRAY || t==Variant::STRING_ARRAY || t==Variant::VECTOR2_ARRAY || t==Variant::VECTOR3_ARRAY || t==Variant::COLOR_ARRAY || t==Variant::RAW_ARRAY) { Variant v = obj->get(n); @@ -3860,6 +3891,7 @@ void PropertyEditor::_bind_methods() { ADD_SIGNAL( MethodInfo("property_toggled",PropertyInfo( Variant::STRING, "property"),PropertyInfo( Variant::BOOL, "value"))); ADD_SIGNAL( MethodInfo("resource_selected", PropertyInfo( Variant::OBJECT, "res"),PropertyInfo( Variant::STRING, "prop") ) ); + ADD_SIGNAL( MethodInfo("object_id_selected", PropertyInfo( Variant::INT, "id")) ); ADD_SIGNAL( MethodInfo("property_keyed",PropertyInfo( Variant::STRING, "property"))); ADD_SIGNAL( MethodInfo("property_edited",PropertyInfo( Variant::STRING, "property"))); } diff --git a/tools/editor/script_editor_debugger.cpp b/tools/editor/script_editor_debugger.cpp index 12840ee4c55..479b694f0b9 100644 --- a/tools/editor/script_editor_debugger.cpp +++ b/tools/editor/script_editor_debugger.cpp @@ -111,6 +111,68 @@ public: } }; + +class ScriptEditorDebuggerInspectedObject : public Object { + + OBJ_TYPE( ScriptEditorDebuggerInspectedObject, Object); + + + + +protected: + + bool _set(const StringName& p_name, const Variant& p_value) { + + if (!prop_values.has(p_name)) + return false; + + emit_signal("value_edited",p_name,p_value); + prop_values[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 *p_list) const { + + p_list->clear(); //sorry, no want category + for (const List::Element *E=prop_list.front();E;E=E->next()) { + p_list->push_back(E->get()); + } + } + + + static void _bind_methods() { + + ADD_SIGNAL(MethodInfo("value_edited")); + } + +public: + + ObjectID last_edited_id; + List prop_list; + Map prop_values; + + void update() { + _change_notify(); + } + + void update_single(const char* p_prop) { + _change_notify(p_prop); + } + + ScriptEditorDebuggerInspectedObject() { last_edited_id=0; } + + +}; + void ScriptEditorDebugger::debug_next() { ERR_FAIL_COND(!breaked); @@ -160,6 +222,72 @@ void ScriptEditorDebugger::debug_continue() { } +void ScriptEditorDebugger::_scene_tree_folded(Object* obj) { + + + if (updating_scene_tree) { + + return; + } + TreeItem *item=obj->cast_to(); + + if (!item) + return; + + ObjectID id = item->get_metadata(0); + if (item->is_collapsed()) { + unfold_cache.erase(id); + } else { + unfold_cache.insert(id); + } + + +} + +void ScriptEditorDebugger::_scene_tree_selected() { + + + if (updating_scene_tree) { + + return; + } + TreeItem *item = inspect_scene_tree->get_selected(); + if (!item) { + + return; + } + + inspected_object_id = item->get_metadata(0); + + Array msg; + msg.push_back("inspect_object"); + msg.push_back(inspected_object_id); + ppeer->put_var(msg); + +} + +void ScriptEditorDebugger::_scene_tree_property_value_edited(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_prop); + msg.push_back(p_value); + ppeer->put_var(msg); + inspect_edited_object_timeout=0.7; //avoid annoyance, don't request soon after editing +} + +void ScriptEditorDebugger::_scene_tree_property_select_object(ObjectID p_object) { + + inspected_object_id=p_object; + Array msg; + msg.push_back("inspect_object"); + msg.push_back(inspected_object_id); + ppeer->put_var(msg); + +} + void ScriptEditorDebugger::_scene_tree_request() { ERR_FAIL_COND(connection.is_null()); @@ -246,10 +374,12 @@ void ScriptEditorDebugger::_parse_message(const String& p_msg,const Array& p_dat } else if (p_msg=="message:scene_tree") { - scene_tree->clear(); + inspect_scene_tree->clear(); Map lv; - for(int i=0;icreate_item(p); + + TreeItem *it = inspect_scene_tree->create_item(p); + + ObjectID id = ObjectID(p_data[i+3]); + it->set_text(0,p_data[i+1]); if (has_icon(p_data[i+2],"EditorIcons")) it->set_icon(0,get_icon(p_data[i+2],"EditorIcons")); + it->set_metadata(0,id); + if (id==inspected_object_id) { + it->select(0); + } + + if (p) { + if (!unfold_cache.has(id)) { + it->set_collapsed(true); + } + } else { + if (unfold_cache.has(id)) { //reverse for root + it->set_collapsed(true); + } + } lv[level]=it; } + updating_scene_tree=false; le_clear->set_disabled(false); le_set->set_disabled(false); + } else if (p_msg=="message:inspect_object") { + + + ObjectID id = p_data[0]; + String type = p_data[1]; + Variant path = p_data[2]; //what to do yet, i don't know + int prop_count=p_data[3]; + + int idx=4; + + + if (inspected_object->last_edited_id!=id) { + inspected_object->prop_list.clear(); + inspected_object->prop_values.clear(); + } + + for(int i=0;ilast_edited_id!=id) { + //don't update.. it's the same, instead refresh + inspected_object->prop_list.push_back(pinfo); + } + + + inspected_object->prop_values[pinfo.name]=p_data[idx++]; + + if (inspected_object->last_edited_id==id) { + //same, just update value, don't rebuild + inspected_object->update_single(pinfo.name.ascii().get_data()); + } + + } + + + + if (inspected_object->last_edited_id!=id) { + //only if different + inspected_object->update(); + } + + inspected_object->last_edited_id=id; + + + inspect_properties->edit(inspected_object); } else if (p_msg=="message:video_mem") { @@ -684,7 +889,7 @@ void ScriptEditorDebugger::_notification(int p_what) { forward->set_icon( get_icon("Forward","EditorIcons")); dobreak->set_icon( get_icon("Pause","EditorIcons")); docontinue->set_icon( get_icon("DebugContinue","EditorIcons")); - scene_tree_refresh->set_icon( get_icon("Reload","EditorIcons")); + //scene_tree_refresh->set_icon( get_icon("Reload","EditorIcons")); le_set->connect("pressed",this,"_live_edit_set"); le_clear->connect("pressed",this,"_live_edit_clear"); error_list->connect("item_selected",this,"_error_selected"); @@ -694,6 +899,36 @@ 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/scene_tree_refresh_interval"); + if (inspect_scene_tree->is_visible()) { + _scene_tree_request(); + + if (inspected_object_id!=0) { + //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); + } + } + } + + 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 (inspect_scene_tree->is_visible() && 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 (error_count!=last_error_count) { if (error_count==0) { @@ -732,7 +967,7 @@ void ScriptEditorDebugger::_notification(int p_what) { reason->set_tooltip(TTR("Child Process Connected")); profiler->clear(); - scene_tree->clear(); + inspect_scene_tree->clear(); le_set->set_disabled(true); le_clear->set_disabled(false); error_list->clear(); @@ -904,6 +1139,9 @@ void ScriptEditorDebugger::stop(){ le_set->set_disabled(true); profiler->set_enabled(true); + inspect_properties->edit(NULL); + inspect_scene_tree->clear(); + EditorNode::get_singleton()->get_pause_button()->set_pressed(false); EditorNode::get_singleton()->get_pause_button()->set_disabled(true); @@ -1191,7 +1429,7 @@ void ScriptEditorDebugger::_live_edit_set() { if (!connection.is_valid()) return; - TreeItem* ti = scene_tree->get_selected(); + TreeItem* ti = inspect_scene_tree->get_selected(); if (!ti) return; String path; @@ -1408,6 +1646,9 @@ void ScriptEditorDebugger::_bind_methods() { ObjectTypeDB::bind_method(_MD("_paused"),&ScriptEditorDebugger::_paused); + ObjectTypeDB::bind_method(_MD("_scene_tree_selected"),&ScriptEditorDebugger::_scene_tree_selected); + ObjectTypeDB::bind_method(_MD("_scene_tree_folded"),&ScriptEditorDebugger::_scene_tree_folded); + ObjectTypeDB::bind_method(_MD("live_debug_create_node"),&ScriptEditorDebugger::live_debug_create_node); ObjectTypeDB::bind_method(_MD("live_debug_instance_node"),&ScriptEditorDebugger::live_debug_instance_node); @@ -1416,6 +1657,8 @@ void ScriptEditorDebugger::_bind_methods() { ObjectTypeDB::bind_method(_MD("live_debug_restore_node"),&ScriptEditorDebugger::live_debug_restore_node); ObjectTypeDB::bind_method(_MD("live_debug_duplicate_node"),&ScriptEditorDebugger::live_debug_duplicate_node); ObjectTypeDB::bind_method(_MD("live_debug_reparent_node"),&ScriptEditorDebugger::live_debug_reparent_node); + ObjectTypeDB::bind_method(_MD("_scene_tree_property_select_object"),&ScriptEditorDebugger::_scene_tree_property_select_object); + ObjectTypeDB::bind_method(_MD("_scene_tree_property_value_edited"),&ScriptEditorDebugger::_scene_tree_property_value_edited); ADD_SIGNAL(MethodInfo("goto_script_line")); ADD_SIGNAL(MethodInfo("breaked",PropertyInfo(Variant::BOOL,"reallydid"),PropertyInfo(Variant::BOOL,"can_debug"))); @@ -1435,249 +1678,284 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor){ add_child(tabs); - VBoxContainer *vbc = memnew( VBoxContainer ); - vbc->set_name(TTR("Debugger")); - //tabs->add_child(vbc); - Control *dbg=vbc; + { //debugger + VBoxContainer *vbc = memnew( VBoxContainer ); + vbc->set_name(TTR("Debugger")); + //tabs->add_child(vbc); + Control *dbg=vbc; - HBoxContainer *hbc = memnew( HBoxContainer ); - vbc->add_child(hbc); + HBoxContainer *hbc = memnew( HBoxContainer ); + vbc->add_child(hbc); - reason = memnew( LineEdit ); - reason->set_text(""); - reason->set_editable(false); - hbc->add_child(reason); - reason->add_color_override("font_color",Color(1,0.4,0.0,0.8)); - reason->set_h_size_flags(SIZE_EXPAND_FILL); - //reason->set_clip_text(true); + reason = memnew( LineEdit ); + reason->set_text(""); + reason->set_editable(false); + hbc->add_child(reason); + reason->add_color_override("font_color",Color(1,0.4,0.0,0.8)); + reason->set_h_size_flags(SIZE_EXPAND_FILL); + //reason->set_clip_text(true); - hbc->add_child( memnew( VSeparator) ); + hbc->add_child( memnew( VSeparator) ); - step = memnew( Button ); - hbc->add_child(step); - step->set_tooltip(TTR("Step Into")); - step->connect("pressed",this,"debug_step"); + step = memnew( Button ); + hbc->add_child(step); + step->set_tooltip(TTR("Step Into")); + step->connect("pressed",this,"debug_step"); - next = memnew( Button ); - hbc->add_child(next); - next->set_tooltip(TTR("Step Over")); - next->connect("pressed",this,"debug_next"); + next = memnew( Button ); + hbc->add_child(next); + next->set_tooltip(TTR("Step Over")); + next->connect("pressed",this,"debug_next"); - hbc->add_child( memnew( VSeparator) ); + hbc->add_child( memnew( VSeparator) ); - dobreak = memnew( Button ); - hbc->add_child(dobreak); - dobreak->set_tooltip(TTR("Break")); - dobreak->connect("pressed",this,"debug_break"); + dobreak = memnew( Button ); + hbc->add_child(dobreak); + dobreak->set_tooltip(TTR("Break")); + dobreak->connect("pressed",this,"debug_break"); - docontinue = memnew( Button ); - hbc->add_child(docontinue); - docontinue->set_tooltip(TTR("Continue")); - docontinue->connect("pressed",this,"debug_continue"); + docontinue = memnew( Button ); + hbc->add_child(docontinue); + docontinue->set_tooltip(TTR("Continue")); + docontinue->connect("pressed",this,"debug_continue"); - hbc->add_child( memnew( VSeparator) ); + hbc->add_child( memnew( VSeparator) ); - back = memnew( Button ); - hbc->add_child(back); - back->set_tooltip(TTR("Inspect Previous Instance")); + back = memnew( Button ); + hbc->add_child(back); + back->set_tooltip(TTR("Inspect Previous Instance")); - forward = memnew( Button ); - hbc->add_child(forward); - forward->set_tooltip(TTR("Inspect Next Instance")); + forward = memnew( Button ); + hbc->add_child(forward); + forward->set_tooltip(TTR("Inspect Next Instance")); - HSplitContainer *sc = memnew( HSplitContainer ); - vbc->add_child(sc); - sc->set_v_size_flags(SIZE_EXPAND_FILL); + HSplitContainer *sc = memnew( HSplitContainer ); + vbc->add_child(sc); + sc->set_v_size_flags(SIZE_EXPAND_FILL); - stack_dump = memnew( Tree ); - stack_dump->set_columns(1); - stack_dump->set_column_titles_visible(true); - stack_dump->set_column_title(0,TTR("Stack Frames")); - stack_dump->set_h_size_flags(SIZE_EXPAND_FILL); - stack_dump->set_hide_root(true); - stack_dump->connect("cell_selected",this,"_stack_dump_frame_selected"); - sc->add_child(stack_dump); + stack_dump = memnew( Tree ); + stack_dump->set_columns(1); + stack_dump->set_column_titles_visible(true); + stack_dump->set_column_title(0,TTR("Stack Frames")); + stack_dump->set_h_size_flags(SIZE_EXPAND_FILL); + stack_dump->set_hide_root(true); + stack_dump->connect("cell_selected",this,"_stack_dump_frame_selected"); + sc->add_child(stack_dump); - inspector = memnew( PropertyEditor ); - inspector->set_h_size_flags(SIZE_EXPAND_FILL); - inspector->hide_top_label(); - inspector->get_scene_tree()->set_column_title(0,TTR("Variable")); - inspector->set_capitalize_paths(false); - inspector->set_read_only(true); - sc->add_child(inspector); + inspector = memnew( PropertyEditor ); + inspector->set_h_size_flags(SIZE_EXPAND_FILL); + inspector->hide_top_label(); + inspector->get_scene_tree()->set_column_title(0,TTR("Variable")); + inspector->set_capitalize_paths(false); + inspector->set_read_only(true); + sc->add_child(inspector); - server = TCP_Server::create_ref(); + server = TCP_Server::create_ref(); - pending_in_queue=0; + pending_in_queue=0; - variables = memnew( ScriptEditorDebuggerVariables ); + variables = memnew( ScriptEditorDebuggerVariables ); - breaked=false; + breaked=false; - tabs->add_child(dbg); - //tabs->move_child(vbc,0); + tabs->add_child(dbg); + //tabs->move_child(vbc,0); - hbc = memnew( HBoxContainer ); - vbc->add_child(hbc); + hbc = memnew( HBoxContainer ); + vbc->add_child(hbc); + + } + + { //errors - error_split = memnew( HSplitContainer ); - VBoxContainer *errvb = memnew( VBoxContainer ); - errvb->set_h_size_flags(SIZE_EXPAND_FILL); - error_list = memnew( ItemList ); - errvb->add_margin_child(TTR("Errors:"),error_list,true); - error_split->add_child(errvb); + error_split = memnew( HSplitContainer ); + VBoxContainer *errvb = memnew( VBoxContainer ); + errvb->set_h_size_flags(SIZE_EXPAND_FILL); + error_list = memnew( ItemList ); + errvb->add_margin_child(TTR("Errors:"),error_list,true); + error_split->add_child(errvb); - errvb = memnew( VBoxContainer ); - errvb->set_h_size_flags(SIZE_EXPAND_FILL); - error_stack = memnew( ItemList ); - errvb->add_margin_child(TTR("Stack Trace (if applicable):"),error_stack,true); - error_split->add_child(errvb); + errvb = memnew( VBoxContainer ); + errvb->set_h_size_flags(SIZE_EXPAND_FILL); + error_stack = memnew( ItemList ); + errvb->add_margin_child(TTR("Stack Trace (if applicable):"),error_stack,true); + error_split->add_child(errvb); - error_split->set_name(TTR("Errors")); - tabs->add_child(error_split); - - profiler = memnew( EditorProfiler ); - profiler->set_name("Profiler"); - tabs->add_child(profiler); - profiler->connect("enable_profiling",this,"_profiler_activate"); - profiler->connect("break_request",this,"_profiler_seeked"); + error_split->set_name(TTR("Errors")); + tabs->add_child(error_split); + } - HSplitContainer *hsp = memnew( HSplitContainer ); + { // inquire - perf_monitors = memnew(Tree); - perf_monitors->set_columns(2); - perf_monitors->set_column_title(0,TTR("Monitor")); - perf_monitors->set_column_title(1,TTR("Value")); - perf_monitors->set_column_titles_visible(true); - hsp->add_child(perf_monitors); - perf_monitors->set_select_mode(Tree::SELECT_MULTI); - perf_monitors->connect("multi_selected",this,"_performance_select"); - perf_draw = memnew( Control ); - perf_draw->connect("draw",this,"_performance_draw"); - hsp->add_child(perf_draw); - hsp->set_name("Metrics"); - hsp->set_split_offset(300); - tabs->add_child(hsp); - perf_max.resize(Performance::MONITOR_MAX); - Map bases; - TreeItem *root=perf_monitors->create_item(); - perf_monitors->set_hide_root(true); - for(int i=0;iset_name(TTR("Remote Inspector")); + tabs->add_child(inspect_info); - String n = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)); - String base = n.get_slice("/",0); - String name = n.get_slice("/",1); - if (!bases.has(base)) { - TreeItem *b = perf_monitors->create_item(root); - b->set_text(0,base.capitalize()); - b->set_editable(0,false); - b->set_selectable(0,false); - bases[base]=b; + VBoxContainer *info_left = memnew(VBoxContainer); + info_left->set_h_size_flags(SIZE_EXPAND_FILL); + inspect_info->add_child(info_left); + + inspect_scene_tree = memnew( Tree ); + info_left->add_margin_child("Live Scene Tree:",inspect_scene_tree,true); + inspect_scene_tree->connect("cell_selected",this,"_scene_tree_selected"); + inspect_scene_tree->connect("item_collapsed",this,"_scene_tree_folded"); + + // + + VBoxContainer *info_right = memnew(VBoxContainer); + info_right->set_h_size_flags(SIZE_EXPAND_FILL); + inspect_info->add_child(info_right); + + inspect_properties = memnew( PropertyEditor ); + inspect_properties->hide_top_label(); + inspect_properties->set_show_categories(true); + inspect_properties->connect("object_id_selected",this,"_scene_tree_property_select_object"); + + info_right->add_margin_child("Remote Object Properties: ",inspect_properties,true); + + inspect_scene_tree_timeout=EDITOR_DEF("debugger/scene_tree_refresh_interval",1.0); + inspect_edited_object_timeout=EDITOR_DEF("debugger/remote_inspect_refresh_interval",0.2); + inspected_object_id=0; + updating_scene_tree=false; + + inspected_object = memnew( ScriptEditorDebuggerInspectedObject ); + inspected_object->connect("value_edited",this,"_scene_tree_property_value_edited"); + } + + { //profiler + profiler = memnew( EditorProfiler ); + profiler->set_name("Profiler"); + tabs->add_child(profiler); + profiler->connect("enable_profiling",this,"_profiler_activate"); + profiler->connect("break_request",this,"_profiler_seeked"); + } + + + { //monitors + + HSplitContainer *hsp = memnew( HSplitContainer ); + + perf_monitors = memnew(Tree); + perf_monitors->set_columns(2); + perf_monitors->set_column_title(0,TTR("Monitor")); + perf_monitors->set_column_title(1,TTR("Value")); + perf_monitors->set_column_titles_visible(true); + hsp->add_child(perf_monitors); + perf_monitors->set_select_mode(Tree::SELECT_MULTI); + perf_monitors->connect("multi_selected",this,"_performance_select"); + perf_draw = memnew( Control ); + perf_draw->connect("draw",this,"_performance_draw"); + hsp->add_child(perf_draw); + hsp->set_name("Monitors"); + hsp->set_split_offset(300); + tabs->add_child(hsp); + perf_max.resize(Performance::MONITOR_MAX); + + Map bases; + TreeItem *root=perf_monitors->create_item(); + perf_monitors->set_hide_root(true); + for(int i=0;iget_monitor_name(Performance::Monitor(i)); + String base = n.get_slice("/",0); + String name = n.get_slice("/",1); + if (!bases.has(base)) { + TreeItem *b = perf_monitors->create_item(root); + b->set_text(0,base.capitalize()); + b->set_editable(0,false); + b->set_selectable(0,false); + bases[base]=b; + } + + TreeItem *it = perf_monitors->create_item(bases[base]); + it->set_editable(0,false); + it->set_selectable(0,true); + it->set_text(0,name.capitalize()); + perf_items.push_back(it); + perf_max[i]=0; + + } + } + + { //vmem inspect + VBoxContainer *vmem_vb = memnew( VBoxContainer ); + HBoxContainer *vmem_hb = memnew( HBoxContainer ); + Label *vmlb = memnew(Label(TTR("List of Video Memory Usage by Resource:")+" ") ); + vmlb->set_h_size_flags(SIZE_EXPAND_FILL); + vmem_hb->add_child( vmlb ); + vmem_hb->add_child( memnew(Label(TTR("Total:")+" ")) ); + vmem_total = memnew( LineEdit ); + vmem_total->set_editable(false); + vmem_total->set_custom_minimum_size(Size2(100,1)); + vmem_hb->add_child(vmem_total); + vmem_refresh = memnew( Button ); + vmem_hb->add_child(vmem_refresh); + vmem_vb->add_child(vmem_hb); + vmem_refresh->connect("pressed",this,"_video_mem_request"); + + MarginContainer *vmmc = memnew( MarginContainer ); + vmem_tree = memnew( Tree ); + vmem_tree->set_v_size_flags(SIZE_EXPAND_FILL); + vmem_tree->set_h_size_flags(SIZE_EXPAND_FILL); + vmmc->add_child(vmem_tree); + vmmc->set_v_size_flags(SIZE_EXPAND_FILL); + vmem_vb->add_child(vmmc); + + vmem_vb->set_name(TTR("Video Mem")); + vmem_tree->set_columns(4); + vmem_tree->set_column_titles_visible(true); + vmem_tree->set_column_title(0,TTR("Resource Path")); + vmem_tree->set_column_expand(0,true); + vmem_tree->set_column_expand(1,false); + vmem_tree->set_column_title(1,TTR("Type")); + vmem_tree->set_column_min_width(1,100); + vmem_tree->set_column_expand(2,false); + vmem_tree->set_column_title(2,TTR("Format")); + vmem_tree->set_column_min_width(2,150); + vmem_tree->set_column_expand(3,false); + vmem_tree->set_column_title(3,TTR("Usage")); + vmem_tree->set_column_min_width(3,80); + vmem_tree->set_hide_root(true); + + tabs->add_child(vmem_vb); + } + + { // misc + VBoxContainer *info_left = memnew( VBoxContainer ); + info_left->set_h_size_flags(SIZE_EXPAND_FILL); + info_left->set_name("Misc"); + tabs->add_child(info_left); + clicked_ctrl = memnew( LineEdit ); + info_left->add_margin_child(TTR("Clicked Control:"),clicked_ctrl); + clicked_ctrl_type = memnew( LineEdit ); + info_left->add_margin_child(TTR("Clicked Control Type:"),clicked_ctrl_type); + + live_edit_root = memnew( LineEdit ); + + { + HBoxContainer *lehb = memnew( HBoxContainer ); + Label *l = memnew( Label(TTR("Live Edit Root:")) ); + lehb->add_child(l); + l->set_h_size_flags(SIZE_EXPAND_FILL); + le_set = memnew( Button(TTR("Set From Tree")) ); + lehb->add_child(le_set); + le_clear = memnew( Button(TTR("Clear")) ); + lehb->add_child(le_clear); + info_left->add_child(lehb); + MarginContainer *mc = memnew( MarginContainer ); + mc->add_child(live_edit_root); + info_left->add_child(mc); + le_set->set_disabled(true); + le_clear->set_disabled(true); } - TreeItem *it = perf_monitors->create_item(bases[base]); - it->set_editable(0,false); - it->set_selectable(0,true); - it->set_text(0,name.capitalize()); - perf_items.push_back(it); - perf_max[i]=0; - } - VBoxContainer *vmem_vb = memnew( VBoxContainer ); - HBoxContainer *vmem_hb = memnew( HBoxContainer ); - Label *vmlb = memnew(Label(TTR("List of Video Memory Usage by Resource:")+" ") ); - vmlb->set_h_size_flags(SIZE_EXPAND_FILL); - vmem_hb->add_child( vmlb ); - vmem_hb->add_child( memnew(Label(TTR("Total:")+" ")) ); - vmem_total = memnew( LineEdit ); - vmem_total->set_editable(false); - vmem_total->set_custom_minimum_size(Size2(100,1)); - vmem_hb->add_child(vmem_total); - vmem_refresh = memnew( Button ); - vmem_hb->add_child(vmem_refresh); - vmem_vb->add_child(vmem_hb); - vmem_refresh->connect("pressed",this,"_video_mem_request"); - - MarginContainer *vmmc = memnew( MarginContainer ); - vmem_tree = memnew( Tree ); - vmem_tree->set_v_size_flags(SIZE_EXPAND_FILL); - vmem_tree->set_h_size_flags(SIZE_EXPAND_FILL); - vmmc->add_child(vmem_tree); - vmmc->set_v_size_flags(SIZE_EXPAND_FILL); - vmem_vb->add_child(vmmc); - - vmem_vb->set_name(TTR("Video Mem")); - vmem_tree->set_columns(4); - vmem_tree->set_column_titles_visible(true); - vmem_tree->set_column_title(0,TTR("Resource Path")); - vmem_tree->set_column_expand(0,true); - vmem_tree->set_column_expand(1,false); - vmem_tree->set_column_title(1,TTR("Type")); - vmem_tree->set_column_min_width(1,100); - vmem_tree->set_column_expand(2,false); - vmem_tree->set_column_title(2,TTR("Format")); - vmem_tree->set_column_min_width(2,150); - vmem_tree->set_column_expand(3,false); - vmem_tree->set_column_title(3,TTR("Usage")); - vmem_tree->set_column_min_width(3,80); - vmem_tree->set_hide_root(true); - - tabs->add_child(vmem_vb); - - info = memnew( HSplitContainer ); - info->set_name(TTR("Info")); - tabs->add_child(info); - - VBoxContainer *info_left = memnew( VBoxContainer ); - info_left->set_h_size_flags(SIZE_EXPAND_FILL); - info->add_child(info_left); - clicked_ctrl = memnew( LineEdit ); - info_left->add_margin_child(TTR("Clicked Control:"),clicked_ctrl); - clicked_ctrl_type = memnew( LineEdit ); - info_left->add_margin_child(TTR("Clicked Control Type:"),clicked_ctrl_type); - - live_edit_root = memnew( LineEdit ); - - { - HBoxContainer *lehb = memnew( HBoxContainer ); - Label *l = memnew( Label(TTR("Live Edit Root:")) ); - lehb->add_child(l); - l->set_h_size_flags(SIZE_EXPAND_FILL); - le_set = memnew( Button(TTR("Set From Tree")) ); - lehb->add_child(le_set); - le_clear = memnew( Button(TTR("Clear")) ); - lehb->add_child(le_clear); - info_left->add_child(lehb); - MarginContainer *mc = memnew( MarginContainer ); - mc->add_child(live_edit_root); - info_left->add_child(mc); - le_set->set_disabled(true); - le_clear->set_disabled(true); - } - - VBoxContainer *info_right = memnew(VBoxContainer); - info_right->set_h_size_flags(SIZE_EXPAND_FILL); - info->add_child(info_right); - HBoxContainer *inforhb = memnew( HBoxContainer ); - info_right->add_child(inforhb); - Label *l2 = memnew( Label(TTR("Scene Tree:") ) ); - l2->set_h_size_flags(SIZE_EXPAND_FILL); - inforhb->add_child( l2 ); - Button *refresh = memnew( Button ); - inforhb->add_child(refresh); - refresh->connect("pressed",this,"_scene_tree_request"); - scene_tree_refresh=refresh; - MarginContainer *infomc = memnew( MarginContainer ); - info_right->add_child(infomc); - infomc->set_v_size_flags(SIZE_EXPAND_FILL); - scene_tree = memnew( Tree ); - infomc->add_child(scene_tree); msgdialog = memnew( AcceptDialog ); @@ -1706,5 +1984,6 @@ ScriptEditorDebugger::~ScriptEditorDebugger() { ppeer->set_stream_peer(Ref()); server->stop(); + memdelete(inspected_object); } diff --git a/tools/editor/script_editor_debugger.h b/tools/editor/script_editor_debugger.h index 12883498842..128ca161730 100644 --- a/tools/editor/script_editor_debugger.h +++ b/tools/editor/script_editor_debugger.h @@ -33,7 +33,7 @@ #include "scene/gui/button.h" #include "core/io/tcp_server.h" #include "core/io/packet_peer.h" - +#include "property_editor.h" class Tree; class PropertyEditor; @@ -49,6 +49,10 @@ class HSplitContainer; class ItemList; class EditorProfiler; + + +class ScriptEditorDebuggerInspectedObject; + class ScriptEditorDebugger : public Control { OBJ_TYPE( ScriptEditorDebugger, Control ); @@ -61,12 +65,23 @@ class ScriptEditorDebugger : public Control { LineEdit *clicked_ctrl; LineEdit *clicked_ctrl_type; LineEdit *live_edit_root; - Tree *scene_tree; - HSplitContainer *info; - Button *scene_tree_refresh; Button *le_set; Button *le_clear; + + + Tree *inspect_scene_tree; + HSplitContainer *inspect_info; + PropertyEditor *inspect_properties; + float inspect_scene_tree_timeout; + float inspect_edited_object_timeout; + ObjectID inspected_object_id; + ScriptEditorDebuggerInspectedObject *inspected_object; + bool updating_scene_tree; + Set unfold_cache; + + + HSplitContainer *error_split; ItemList *error_list; ItemList *error_stack; @@ -132,8 +147,13 @@ class ScriptEditorDebugger : public Control { void _stack_dump_frame_selected(); void _output_clear(); + + void _scene_tree_folded(Object* obj); + void _scene_tree_selected(); void _scene_tree_request(); void _parse_message(const String& p_msg,const Array& p_data); + void _scene_tree_property_select_object(ObjectID p_object); + void _scene_tree_property_value_edited(const String& p_prop,const Variant& p_value); void _video_mem_request();