diff options
Diffstat (limited to 'editor')
82 files changed, 10258 insertions, 1147 deletions
diff --git a/editor/animation_editor.cpp b/editor/animation_editor.cpp index f7c8cac93f..a03bf76d1b 100644 --- a/editor/animation_editor.cpp +++ b/editor/animation_editor.cpp @@ -2969,6 +2969,7 @@ void AnimationKeyEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { + update_keying(); EditorNode::get_singleton()->update_keying(); emit_signal("keying_changed"); } break; diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 24e86770bf..665ce7658f 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -373,7 +373,6 @@ void FindReplaceBar::_hide_bar() { void FindReplaceBar::_show_search() { - hide(); // to update size correctly show(); search_text->grab_focus(); @@ -481,7 +480,7 @@ void FindReplaceBar::set_text_edit(TextEdit *p_text_edit) { void FindReplaceBar::_update_size() { - container->set_custom_minimum_size(Size2(0, hbc->get_size().height)); + container->set_size(Size2(hbc->get_size().width, 1)); } void FindReplaceBar::_bind_methods() { @@ -507,7 +506,8 @@ void FindReplaceBar::_bind_methods() { FindReplaceBar::FindReplaceBar() { - container = memnew(Control); + container = memnew(MarginContainer); + container->add_constant_override("margin_bottom", 5 * EDSCALE); add_child(container); container->set_clip_contents(true); container->set_h_size_flags(SIZE_EXPAND_FILL); @@ -592,8 +592,7 @@ FindReplaceBar::FindReplaceBar() { add_child(hide_button); hide_button->set_focus_mode(FOCUS_NONE); hide_button->connect("pressed", this, "_hide_pressed"); - hide_button->set_expand(true); - hide_button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED); + hide_button->set_v_size_flags(SIZE_SHRINK_CENTER); } /*** CODE EDITOR ****/ @@ -779,6 +778,7 @@ void CodeTextEditor::update_editor_settings() { text_editor->set_draw_breakpoint_gutter(EditorSettings::get_singleton()->get("text_editor/line_numbers/show_breakpoint_gutter")); text_editor->set_hiding_enabled(EditorSettings::get_singleton()->get("text_editor/line_numbers/code_folding")); text_editor->set_draw_fold_gutter(EditorSettings::get_singleton()->get("text_editor/line_numbers/code_folding")); + text_editor->set_wrap_enabled(EditorSettings::get_singleton()->get("text_editor/line_numbers/word_wrap")); text_editor->cursor_set_block_mode(EditorSettings::get_singleton()->get("text_editor/cursor/block_caret")); text_editor->set_smooth_scroll_enabled(EditorSettings::get_singleton()->get("text_editor/open_scripts/smooth_scrolling")); text_editor->set_v_scroll_speed(EditorSettings::get_singleton()->get("text_editor/open_scripts/v_scroll_speed")); diff --git a/editor/code_editor.h b/editor/code_editor.h index a860ad24e2..2a3bb1ba76 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -63,7 +63,7 @@ class FindReplaceBar : public HBoxContainer { GDCLASS(FindReplaceBar, HBoxContainer); - Control *container; + MarginContainer *container; LineEdit *search_text; ToolButton *find_prev; ToolButton *find_next; diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 953d787322..c4a17d5402 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -504,25 +504,25 @@ void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector< void DependencyRemoveDialog::ok_pressed() { + for (int i = 0; i < files_to_delete.size(); ++i) { + if (ResourceCache::has(files_to_delete[i])) { + Resource *res = ResourceCache::get(files_to_delete[i]); + res->set_path(""); + } + String path = OS::get_singleton()->get_resource_dir() + files_to_delete[i].replace_first("res://", "/"); + print_line("Moving to trash: " + path); + Error err = OS::get_singleton()->move_to_trash(path); + if (err != OK) { + EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + files_to_delete[i] + "\n"); + } + } + if (dirs_to_delete.size() == 0) { //If we only deleted files we should only need to tell the file system about the files we touched. for (int i = 0; i < files_to_delete.size(); ++i) EditorFileSystem::get_singleton()->update_file(files_to_delete[i]); } else { - for (int i = 0; i < files_to_delete.size(); ++i) { - if (ResourceCache::has(files_to_delete[i])) { - Resource *res = ResourceCache::get(files_to_delete[i]); - res->set_path(""); - } - String path = OS::get_singleton()->get_resource_dir() + files_to_delete[i].replace_first("res://", "/"); - print_line("Moving to trash: " + path); - Error err = OS::get_singleton()->move_to_trash(path); - if (err != OK) { - EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + files_to_delete[i] + "\n"); - } - } - for (int i = 0; i < dirs_to_delete.size(); ++i) { String path = OS::get_singleton()->get_resource_dir() + dirs_to_delete[i].replace_first("res://", "/"); print_line("Moving to trash: " + path); diff --git a/editor/doc/doc_data.cpp b/editor/doc/doc_data.cpp index 3434aa33f9..c992ac5f16 100644 --- a/editor/doc/doc_data.cpp +++ b/editor/doc/doc_data.cpp @@ -567,6 +567,9 @@ void DocData::generate(bool p_basic_types) { PropertyDoc pd; Engine::Singleton &s = E->get(); + if (!s.ptr) { + continue; + } pd.name = s.name; pd.type = s.ptr->get_class(); while (String(ClassDB::get_parent_class(pd.type)) != "Object") diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index a2f5c1aa1a..de9203232c 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -33,6 +33,8 @@ #include "editor_node.h" #include "global_constants.h" #include "project_settings.h" +#include "scene/main/viewport.h" +#include "scene/resources/packed_scene.h" #define PREVIEW_LIST_MAX_SIZE 10 @@ -50,6 +52,13 @@ void EditorAutoloadSettings::_notification(int p_what) { file_dialog->add_filter("*." + E->get()); } + + for (List<AutoLoadInfo>::Element *E = autoload_cache.front(); E; E = E->next()) { + AutoLoadInfo &info = E->get(); + if (info.node && info.in_editor) { + get_tree()->get_root()->call_deferred("add_child", info.node); + } + } } } @@ -155,8 +164,8 @@ void EditorAutoloadSettings::_autoload_edited() { undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", selected_autoload, order); undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", name); - undo_redo->add_do_method(this, "update_autoload"); - undo_redo->add_undo_method(this, "update_autoload"); + undo_redo->add_do_method(this, "call_deferred", "update_autoload"); + undo_redo->add_undo_method(this, "call_deferred", "update_autoload"); undo_redo->add_do_method(this, "emit_signal", autoload_changed); undo_redo->add_undo_method(this, "emit_signal", autoload_changed); @@ -187,8 +196,8 @@ void EditorAutoloadSettings::_autoload_edited() { undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", base, order); undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", base, order); - undo_redo->add_do_method(this, "update_autoload"); - undo_redo->add_undo_method(this, "update_autoload"); + undo_redo->add_do_method(this, "call_deferred", "update_autoload"); + undo_redo->add_undo_method(this, "call_deferred", "update_autoload"); undo_redo->add_do_method(this, "emit_signal", autoload_changed); undo_redo->add_undo_method(this, "emit_signal", autoload_changed); @@ -289,6 +298,36 @@ void EditorAutoloadSettings::_autoload_file_callback(const String &p_path) { autoload_add_name->set_text(p_path.get_file().get_basename()); } +Node *EditorAutoloadSettings::_create_autoload(const String &p_path) { + RES res = ResourceLoader::load(p_path); + ERR_EXPLAIN("Can't autoload: " + p_path); + ERR_FAIL_COND_V(res.is_null(), NULL); + Node *n = NULL; + if (res->is_class("PackedScene")) { + Ref<PackedScene> ps = res; + n = ps->instance(); + } else if (res->is_class("Script")) { + Ref<Script> s = res; + StringName ibt = s->get_instance_base_type(); + bool valid_type = ClassDB::is_parent_class(ibt, "Node"); + ERR_EXPLAIN("Script does not inherit a Node: " + p_path); + ERR_FAIL_COND_V(!valid_type, NULL); + + Object *obj = ClassDB::instance(ibt); + + ERR_EXPLAIN("Cannot instance script for autoload, expected 'Node' inheritance, got: " + String(ibt)); + ERR_FAIL_COND_V(obj == NULL, NULL); + + n = Object::cast_to<Node>(obj); + n->set_script(s.get_ref_ptr()); + } + + ERR_EXPLAIN("Path in autoload not a node or script: " + p_path); + ERR_FAIL_COND_V(!n, NULL); + + return n; +} + void EditorAutoloadSettings::update_autoload() { if (updating_autoload) @@ -296,6 +335,14 @@ void EditorAutoloadSettings::update_autoload() { updating_autoload = true; + Map<String, AutoLoadInfo> to_remove; + List<AutoLoadInfo *> to_add; + + for (List<AutoLoadInfo>::Element *E = autoload_cache.front(); E; E = E->next()) { + AutoLoadInfo &info = E->get(); + to_remove.insert(info.name, info); + } + autoload_cache.clear(); tree->clear(); @@ -318,16 +365,39 @@ void EditorAutoloadSettings::update_autoload() { continue; AutoLoadInfo info; - info.name = pi.name; + info.is_singleton = path.begins_with("*"); + + if (info.is_singleton) { + path = path.substr(1, path.length()); + } + + info.name = name; + info.path = path; info.order = ProjectSettings::get_singleton()->get_order(pi.name); - autoload_cache.push_back(info); + bool need_to_add = true; + if (to_remove.has(name)) { + AutoLoadInfo &old_info = to_remove[name]; + if (old_info.path == info.path) { + // Still the same resource, check status + info.node = old_info.node; + if (info.node) { + Ref<Script> scr = info.node->get_script(); + info.in_editor = scr.is_valid() && scr->is_tool(); + if (info.is_singleton == old_info.is_singleton && info.in_editor == old_info.in_editor) { + to_remove.erase(name); + need_to_add = false; + } else { + info.node = NULL; + } + } + } + } - bool global = false; + autoload_cache.push_back(info); - if (path.begins_with("*")) { - global = true; - path = path.substr(1, path.length()); + if (need_to_add) { + to_add.push_back(&(autoload_cache.back()->get())); } TreeItem *item = tree->create_item(root); @@ -340,7 +410,7 @@ void EditorAutoloadSettings::update_autoload() { item->set_cell_mode(2, TreeItem::CELL_MODE_CHECK); item->set_editable(2, true); item->set_text(2, TTR("Enable")); - item->set_checked(2, global); + item->set_checked(2, info.is_singleton); item->add_button(3, get_icon("FileList", "EditorIcons"), BUTTON_OPEN); item->add_button(3, get_icon("MoveUp", "EditorIcons"), BUTTON_MOVE_UP); item->add_button(3, get_icon("MoveDown", "EditorIcons"), BUTTON_MOVE_DOWN); @@ -348,6 +418,60 @@ void EditorAutoloadSettings::update_autoload() { item->set_selectable(3, false); } + // Remove deleted/changed autoloads + for (Map<String, AutoLoadInfo>::Element *E = to_remove.front(); E; E = E->next()) { + AutoLoadInfo &info = E->get(); + if (info.is_singleton) { + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptServer::get_language(i)->remove_named_global_constant(info.name); + } + } + if (info.in_editor) { + ERR_CONTINUE(!info.node); + get_tree()->get_root()->remove_child(info.node); + } + + if (info.node) { + memdelete(info.node); + info.node = NULL; + } + } + + // Load new/changed autoloads + List<Node *> nodes_to_add; + for (List<AutoLoadInfo *>::Element *E = to_add.front(); E; E = E->next()) { + AutoLoadInfo *info = E->get(); + + info->node = _create_autoload(info->path); + + ERR_CONTINUE(!info->node); + info->node->set_name(info->name); + + Ref<Script> scr = info->node->get_script(); + info->in_editor = scr.is_valid() && scr->is_tool(); + + if (info->in_editor) { + //defer so references are all valid on _ready() + nodes_to_add.push_back(info->node); + } + + if (info->is_singleton) { + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptServer::get_language(i)->add_named_global_constant(info->name, info->node); + } + } + + if (!info->in_editor && !info->is_singleton) { + // No reason to keep this node + memdelete(info->node); + info->node = NULL; + } + } + + for (List<Node *>::Element *E = nodes_to_add.front(); E; E = E->next()) { + get_tree()->get_root()->add_child(E->get()); + } + updating_autoload = false; } @@ -592,6 +716,54 @@ void EditorAutoloadSettings::_bind_methods() { EditorAutoloadSettings::EditorAutoloadSettings() { + // Make first cache + List<PropertyInfo> props; + ProjectSettings::get_singleton()->get_property_list(&props); + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + + const PropertyInfo &pi = E->get(); + + if (!pi.name.begins_with("autoload/")) + continue; + + String name = pi.name.get_slice("/", 1); + String path = ProjectSettings::get_singleton()->get(pi.name); + + if (name.empty()) + continue; + + AutoLoadInfo info; + info.is_singleton = path.begins_with("*"); + + if (info.is_singleton) { + path = path.substr(1, path.length()); + } + + info.name = name; + info.path = path; + info.order = ProjectSettings::get_singleton()->get_order(pi.name); + info.node = _create_autoload(path); + + if (info.node) { + Ref<Script> scr = info.node->get_script(); + info.in_editor = scr.is_valid() && scr->is_tool(); + info.node->set_name(info.name); + } + + if (info.is_singleton) { + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptServer::get_language(i)->add_named_global_constant(info.name, info.node); + } + } + + if (!info.is_singleton && !info.in_editor) { + memdelete(info.node); + info.node = NULL; + } + + autoload_cache.push_back(info); + } + autoload_changed = "autoload_changed"; updating_autoload = false; @@ -656,3 +828,12 @@ EditorAutoloadSettings::EditorAutoloadSettings() { add_child(tree, true); } + +EditorAutoloadSettings::~EditorAutoloadSettings() { + for (List<AutoLoadInfo>::Element *E = autoload_cache.front(); E; E = E->next()) { + AutoLoadInfo &info = E->get(); + if (info.node && !info.in_editor) { + memdelete(info.node); + } + } +} diff --git a/editor/editor_autoload_settings.h b/editor/editor_autoload_settings.h index 6f622de6d5..0b75faa009 100644 --- a/editor/editor_autoload_settings.h +++ b/editor/editor_autoload_settings.h @@ -50,11 +50,21 @@ class EditorAutoloadSettings : public VBoxContainer { struct AutoLoadInfo { String name; + String path; + bool is_singleton; + bool in_editor; int order; + Node *node; bool operator==(const AutoLoadInfo &p_info) { return order == p_info.order; } + + AutoLoadInfo() { + is_singleton = false; + in_editor = false; + node = NULL; + } }; List<AutoLoadInfo> autoload_cache; @@ -76,6 +86,7 @@ class EditorAutoloadSettings : public VBoxContainer { void _autoload_activated(); void _autoload_open(const String &fpath); void _autoload_file_callback(const String &p_path); + Node *_create_autoload(const String &p_path); Variant get_drag_data_fw(const Point2 &p_point, Control *p_control); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_control) const; @@ -91,6 +102,7 @@ public: void autoload_remove(const String &p_name); EditorAutoloadSettings(); + ~EditorAutoloadSettings(); }; #endif diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index b584107bcb..d41d5c929a 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -420,6 +420,18 @@ void EditorData::paste_object_params(Object *p_object) { } } +bool EditorData::call_build() { + + bool result = true; + + for (int i = 0; i < editor_plugins.size() && result; i++) { + + result &= editor_plugins[i]->build(); + } + + return result; +} + UndoRedo &EditorData::get_undo_redo() { return undo_redo; @@ -898,7 +910,7 @@ Array EditorSelection::_get_transformable_selected_nodes() { return ret; } -Array EditorSelection::_get_selected_nodes() { +Array EditorSelection::get_selected_nodes() { Array ret; @@ -916,7 +928,7 @@ void EditorSelection::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &EditorSelection::clear); ClassDB::bind_method(D_METHOD("add_node", "node"), &EditorSelection::add_node); ClassDB::bind_method(D_METHOD("remove_node", "node"), &EditorSelection::remove_node); - ClassDB::bind_method(D_METHOD("get_selected_nodes"), &EditorSelection::_get_selected_nodes); + ClassDB::bind_method(D_METHOD("get_selected_nodes"), &EditorSelection::get_selected_nodes); ClassDB::bind_method(D_METHOD("get_transformable_selected_nodes"), &EditorSelection::_get_transformable_selected_nodes); ClassDB::bind_method(D_METHOD("_emit_change"), &EditorSelection::_emit_change); ADD_SIGNAL(MethodInfo("selection_changed")); diff --git a/editor/editor_data.h b/editor/editor_data.h index 5a0b58464a..0452867bf4 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -197,6 +197,7 @@ public: NodePath get_edited_scene_live_edit_root(); bool check_and_update_scene(int p_idx); void move_edited_scene_to_index(int p_idx); + bool call_build(); void set_plugin_window_layout(Ref<ConfigFile> p_layout); void get_plugin_window_layout(Ref<ConfigFile> p_layout); @@ -226,7 +227,6 @@ private: List<Node *> selected_node_list; void _update_nl(); - Array _get_selected_nodes(); Array _get_transformable_selected_nodes(); void _emit_change(); @@ -234,6 +234,7 @@ protected: static void _bind_methods(); public: + Array get_selected_nodes(); void add_node(Node *p_node); void remove_node(Node *p_node); bool is_selected(Node *) const; diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index f3be02a8c7..b49c2d26d0 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -526,18 +526,7 @@ void EditorHelp::_unhandled_key_input(const Ref<InputEvent> &p_ev) { void EditorHelp::_search(const String &) { - if (search->get_text() == "") - return; - - String stext = search->get_text(); - bool keep = prev_search == stext; - - bool ret = class_desc->search(stext, keep); - if (!ret) { - class_desc->search(stext, false); - } - - prev_search = stext; + find_bar->search_next(); } void EditorHelp::_class_list_select(const String &p_select) { @@ -598,14 +587,6 @@ void EditorHelp::_class_desc_select(const String &p_select) { } void EditorHelp::_class_desc_input(const Ref<InputEvent> &p_input) { - - Ref<InputEventMouseButton> mb = p_input; - - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == 1 && !mb->is_doubleclick()) { - class_desc->set_selection_enabled(false); - class_desc->set_selection_enabled(true); - } - set_focused(); } void EditorHelp::_add_type(const String &p_type, const String &p_enum) { @@ -1816,13 +1797,7 @@ void EditorHelp::scroll_to_section(int p_section_index) { void EditorHelp::popup_search() { - search_dialog->popup_centered(Size2(250, 80) * EDSCALE); - search->grab_focus(); -} - -void EditorHelp::_search_cbk() { - - _search(search->get_text()); + find_bar->popup_search(); } String EditorHelp::get_class() { @@ -1851,7 +1826,6 @@ void EditorHelp::_bind_methods() { ClassDB::bind_method("_request_help", &EditorHelp::_request_help); ClassDB::bind_method("_unhandled_key_input", &EditorHelp::_unhandled_key_input); ClassDB::bind_method("_search", &EditorHelp::_search); - ClassDB::bind_method("_search_cbk", &EditorHelp::_search_cbk); ClassDB::bind_method("_help_callback", &EditorHelp::_help_callback); ADD_SIGNAL(MethodInfo("go_to_help")); @@ -1863,6 +1837,10 @@ EditorHelp::EditorHelp() { EDITOR_DEF("text_editor/help/sort_functions_alphabetically", true); + find_bar = memnew(FindBar); + add_child(find_bar); + find_bar->hide(); + class_desc = memnew(RichTextLabel); add_child(class_desc); class_desc->set_v_size_flags(SIZE_EXPAND_FILL); @@ -1870,24 +1848,14 @@ EditorHelp::EditorHelp() { class_desc->connect("meta_clicked", this, "_class_desc_select"); class_desc->connect("gui_input", this, "_class_desc_input"); + find_bar->set_rich_text_label(class_desc); + class_desc->set_selection_enabled(true); scroll_locked = false; select_locked = false; - set_process_unhandled_key_input(true); + //set_process_unhandled_key_input(true); class_desc->hide(); - - search_dialog = memnew(ConfirmationDialog); - add_child(search_dialog); - VBoxContainer *search_vb = memnew(VBoxContainer); - search_dialog->add_child(search_vb); - - search = memnew(LineEdit); - search_dialog->register_text_enter(search); - search_vb->add_margin_child(TTR("Search Text"), search); - search_dialog->get_ok()->set_text(TTR("Find")); - search_dialog->connect("confirmed", this, "_search_cbk"); - search_dialog->set_hide_on_ok(false); } EditorHelp::~EditorHelp() { @@ -1964,3 +1932,197 @@ EditorHelpBit::EditorHelpBit() { rich_text->set_override_selected_font_color(false); set_custom_minimum_size(Size2(0, 70 * EDSCALE)); } + +FindBar::FindBar() { + + container = memnew(Control); + add_child(container); + + container->set_clip_contents(true); + container->set_h_size_flags(SIZE_EXPAND_FILL); + + hbc = memnew(HBoxContainer); + container->add_child(hbc); + + vbc_search_text = memnew(VBoxContainer); + hbc->add_child(vbc_search_text); + vbc_search_text->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->set_anchor_and_margin(MARGIN_RIGHT, 1, 0); + + search_text = memnew(LineEdit); + vbc_search_text->add_child(search_text); + search_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + search_text->connect("text_changed", this, "_search_text_changed"); + search_text->connect("text_entered", this, "_search_text_entered"); + + find_prev = memnew(ToolButton); + hbc->add_child(find_prev); + find_prev->set_focus_mode(FOCUS_NONE); + find_prev->connect("pressed", this, "_search_prev"); + + find_next = memnew(ToolButton); + hbc->add_child(find_next); + find_next->set_focus_mode(FOCUS_NONE); + find_next->connect("pressed", this, "_search_next"); + + error_label = memnew(Label); + hbc->add_child(error_label); + error_label->add_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor")); + + hide_button = memnew(TextureButton); + add_child(hide_button); + hide_button->set_focus_mode(FOCUS_NONE); + hide_button->set_expand(true); + hide_button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED); + hide_button->connect("pressed", this, "_hide_pressed"); +} + +void FindBar::popup_search() { + + show(); + bool grabbed_focus = false; + if (!search_text->has_focus()) { + search_text->grab_focus(); + grabbed_focus = true; + } + + if (!search_text->get_text().empty()) { + search_text->select_all(); + search_text->set_cursor_position(search_text->get_text().length()); + if (grabbed_focus) { + _search(); + } + } + + call_deferred("_update_size"); +} + +void FindBar::_update_size() { + + container->set_custom_minimum_size(Size2(0, hbc->get_size().height)); +} + +void FindBar::_notification(int p_what) { + + if (p_what == NOTIFICATION_READY) { + + find_prev->set_icon(get_icon("MoveUp", "EditorIcons")); + find_next->set_icon(get_icon("MoveDown", "EditorIcons")); + hide_button->set_normal_texture(get_icon("Close", "EditorIcons")); + hide_button->set_hover_texture(get_icon("Close", "EditorIcons")); + hide_button->set_pressed_texture(get_icon("Close", "EditorIcons")); + hide_button->set_custom_minimum_size(hide_button->get_normal_texture()->get_size()); + } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + + set_process_unhandled_input(is_visible_in_tree()); + } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { + + find_prev->set_icon(get_icon("MoveUp", "EditorIcons")); + find_next->set_icon(get_icon("MoveDown", "EditorIcons")); + hide_button->set_normal_texture(get_icon("Close", "EditorIcons")); + hide_button->set_hover_texture(get_icon("Close", "EditorIcons")); + hide_button->set_pressed_texture(get_icon("Close", "EditorIcons")); + hide_button->set_custom_minimum_size(hide_button->get_normal_texture()->get_size()); + } +} + +void FindBar::_bind_methods() { + + ClassDB::bind_method("_unhandled_input", &FindBar::_unhandled_input); + + ClassDB::bind_method("_search_text_changed", &FindBar::_search_text_changed); + ClassDB::bind_method("_search_text_entered", &FindBar::_search_text_entered); + ClassDB::bind_method("_search_next", &FindBar::search_next); + ClassDB::bind_method("_search_prev", &FindBar::search_prev); + ClassDB::bind_method("_hide_pressed", &FindBar::_hide_bar); + ClassDB::bind_method("_update_size", &FindBar::_update_size); + + ADD_SIGNAL(MethodInfo("search")); +} + +void FindBar::set_rich_text_label(RichTextLabel *p_rich_text_label) { + + rich_text_label = p_rich_text_label; +} + +bool FindBar::search_next() { + + return _search(); +} + +bool FindBar::search_prev() { + + return _search(true); +} + +bool FindBar::_search(bool p_search_previous) { + + String stext = search_text->get_text(); + bool keep = prev_search == stext; + + bool ret = rich_text_label->search(stext, keep, p_search_previous); + if (!ret) { + ret = rich_text_label->search(stext, false, p_search_previous); + } + + prev_search = stext; + + if (ret) { + set_error(""); + } else { + set_error(stext.empty() ? "" : TTR("No Matches")); + } + + return ret; +} + +void FindBar::set_error(const String &p_label) { + + error_label->set_text(p_label); +} + +void FindBar::_hide_bar() { + + if (search_text->has_focus()) + rich_text_label->grab_focus(); + + hide(); +} + +void FindBar::_unhandled_input(const Ref<InputEvent> &p_event) { + + Ref<InputEventKey> k = p_event; + if (k.is_valid()) { + + if (k->is_pressed() && (rich_text_label->has_focus() || hbc->is_a_parent_of(get_focus_owner()))) { + + bool accepted = true; + + switch (k->get_scancode()) { + + case KEY_ESCAPE: { + + _hide_bar(); + } break; + default: { + + accepted = false; + } break; + } + + if (accepted) { + accept_event(); + } + } + } +} + +void FindBar::_search_text_changed(const String &p_text) { + + search_next(); +} + +void FindBar::_search_text_entered(const String &p_text) { + + search_next(); +} diff --git a/editor/editor_help.h b/editor/editor_help.h index 0f93e1b55b..514169dc19 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -125,6 +125,52 @@ public: EditorHelpIndex(); }; +class FindBar : public HBoxContainer { + + GDCLASS(FindBar, HBoxContainer); + + LineEdit *search_text; + ToolButton *find_prev; + ToolButton *find_next; + Label *error_label; + TextureButton *hide_button; + String prev_search; + + Control *container; + HBoxContainer *hbc; + VBoxContainer *vbc_search_text; + + RichTextLabel *rich_text_label; + + void _show_search(); + void _hide_bar(); + + void _search_text_changed(const String &p_text); + void _search_text_entered(const String &p_text); + + void _update_size(); + +protected: + void _notification(int p_what); + void _unhandled_input(const Ref<InputEvent> &p_event); + + bool _search(bool p_search_previous = false); + + static void _bind_methods(); + +public: + void set_error(const String &p_label); + + void set_rich_text_label(RichTextLabel *p_rich_text_label); + + void popup_search(); + + bool search_prev(); + bool search_next(); + + FindBar(); +}; + class EditorHelp : public VBoxContainer { GDCLASS(EditorHelp, VBoxContainer); @@ -161,6 +207,7 @@ class EditorHelp : public VBoxContainer { ConfirmationDialog *search_dialog; LineEdit *search; + FindBar *find_bar; String base_path; @@ -194,7 +241,6 @@ class EditorHelp : public VBoxContainer { void _request_help(const String &p_string); void _search(const String &p_str); - void _search_cbk(); void _unhandled_key_input(const Ref<InputEvent> &p_ev); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp new file mode 100644 index 0000000000..f94b7cd6ee --- /dev/null +++ b/editor/editor_inspector.cpp @@ -0,0 +1,1987 @@ +/*************************************************************************/ +/* editor_inspector.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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_inspector.h" +#include "array_property_edit.h" +#include "dictionary_property_edit.h" +#include "editor_node.h" +#include "editor_scale.h" +#include "multi_node_edit.h" +#include "scene/resources/packed_scene.h" + +// TODO: +// arrays and dictionary +// replace property editor in sectionedpropertyeditor + +Size2 EditorProperty::get_minimum_size() const { + + Size2 ms; + for (int i = 0; i < get_child_count(); i++) { + + Control *c = Object::cast_to<Control>(get_child(i)); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + if (!c->is_visible()) + continue; + if (c == bottom_editor) + continue; + + Size2 minsize = c->get_combined_minimum_size(); + ms.width = MAX(ms.width, minsize.width); + ms.height = MAX(ms.height, minsize.height); + } + + if (keying) { + Ref<Texture> key = get_icon("Key", "EditorIcons"); + ms.width += key->get_width() + get_constant("hseparator", "Tree"); + } + + if (checkable) { + Ref<Texture> check = get_icon("checked", "CheckBox"); + ms.width += check->get_width() + get_constant("hseparator", "Tree"); + } + + if (bottom_editor != NULL) { + Ref<Font> font = get_font("font", "Tree"); + ms.height += font->get_height(); + ms.height += get_constant("vseparation", "Tree"); + Size2 bems = bottom_editor->get_combined_minimum_size(); + bems.width += get_constant("item_margin", "Tree"); + ms.height += bems.height; + ms.width = MAX(ms.width, bems.width); + } + + return ms; +} + +void EditorProperty::_notification(int p_what) { + + if (p_what == NOTIFICATION_SORT_CHILDREN) { + + Size2 size = get_size(); + Rect2 rect; + Rect2 bottom_rect; + + { + int child_room = size.width / 2; + Ref<Font> font = get_font("font", "Tree"); + int height = font->get_height(); + + //compute room needed + for (int i = 0; i < get_child_count(); i++) { + + Control *c = Object::cast_to<Control>(get_child(i)); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + if (c == bottom_editor) + continue; + + Size2 minsize = c->get_combined_minimum_size(); + child_room = MAX(child_room, minsize.width); + height = MAX(height, minsize.height); + } + + text_size = MAX(0, size.width - child_room + 4 * EDSCALE); + + rect = Rect2(text_size, 0, size.width - text_size, height); + + if (bottom_editor) { + + int m = get_constant("item_margin", "Tree"); + bottom_rect = Rect2(m, rect.size.height + get_constant("vseparation", "Tree"), size.width - m, bottom_editor->get_combined_minimum_size().height); + } + } + + if (keying) { + Ref<Texture> key; + + if (use_keying_next()) { + key = get_icon("KeyNext", "EditorIcons"); + } else { + key = get_icon("Key", "EditorIcons"); + } + + rect.size.x -= key->get_width() + get_constant("hseparator", "Tree"); + } + + //set children + for (int i = 0; i < get_child_count(); i++) { + + Control *c = Object::cast_to<Control>(get_child(i)); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + if (c == bottom_editor) + continue; + + fit_child_in_rect(c, rect); + } + + if (bottom_editor) { + fit_child_in_rect(bottom_editor, bottom_rect); + } + + update(); //need to redraw text + } + + if (p_what == NOTIFICATION_DRAW) { + Ref<Font> font = get_font("font", "Tree"); + + Size2 size = get_size(); + if (bottom_editor) { + size.height = bottom_editor->get_margin(MARGIN_TOP); + } else if (label_reference) { + size.height = label_reference->get_size().height; + } + + if (selected) { + Ref<StyleBox> sb = get_stylebox("selected", "Tree"); + draw_style_box(sb, Rect2(Vector2(), size)); + } + + Color color; + if (draw_red) { + color = get_color("error_color", "Editor"); + } else { + color = get_color("font_color", "Tree"); + } + if (label.find(".") != -1) { + color.a = 0.5; //this should be un-hacked honestly, as it's used for editor overrides + } + + int ofs = 0; + if (checkable) { + Ref<Texture> checkbox; + if (checked) + checkbox = get_icon("checked", "CheckBox"); + else + checkbox = get_icon("unchecked", "CheckBox"); + + Color color(1, 1, 1); + if (check_hover) { + color.r *= 1.2; + color.g *= 1.2; + color.b *= 1.2; + } + check_rect = Rect2(ofs, ((size.height - checkbox->get_height()) / 2), checkbox->get_width(), checkbox->get_height()); + draw_texture(checkbox, check_rect.position, color); + ofs += get_constant("hseparator", "Tree"); + ofs += checkbox->get_width(); + } else { + check_rect = Rect2(); + } + + int text_limit = text_size; + + if (can_revert) { + Ref<Texture> reload_icon = get_icon("ReloadSmall", "EditorIcons"); + text_limit -= reload_icon->get_width() + get_constant("hseparator", "Tree") * 2; + revert_rect = Rect2(text_limit + get_constant("hseparator", "Tree"), (size.height - reload_icon->get_height()) / 2, reload_icon->get_width(), reload_icon->get_height()); + + Color color(1, 1, 1); + if (revert_hover) { + color.r *= 1.2; + color.g *= 1.2; + color.b *= 1.2; + } + + draw_texture(reload_icon, revert_rect.position, color); + } else { + revert_rect = Rect2(); + } + + int v_ofs = (size.height - font->get_height()) / 2; + draw_string(font, Point2(ofs, v_ofs + font->get_ascent()), label, color, text_limit); + + if (keying) { + Ref<Texture> key; + + if (use_keying_next()) { + key = get_icon("KeyNext", "EditorIcons"); + } else { + key = get_icon("Key", "EditorIcons"); + } + + ofs = size.width - key->get_width() - get_constant("hseparator", "Tree"); + + Color color(1, 1, 1); + if (keying_hover) { + color.r *= 1.2; + color.g *= 1.2; + color.b *= 1.2; + } + keying_rect = Rect2(ofs, ((size.height - key->get_height()) / 2), key->get_width(), key->get_height()); + draw_texture(key, keying_rect.position, color); + } else { + keying_rect = Rect2(); + } + + //int vs = get_constant("vseparation", "Tree"); + Color guide_color = get_color("guide_color", "Tree"); + int vs_height = get_size().height; // vs / 2; + draw_line(Point2(0, vs_height), Point2(get_size().width, vs_height), guide_color); + } +} + +void EditorProperty::set_label(const String &p_label) { + label = p_label; + update(); +} + +String EditorProperty::get_label() const { + return label; +} + +Object *EditorProperty::get_edited_object() { + return object; +} + +StringName EditorProperty::get_edited_property() { + return property; +} + +void EditorProperty::update_property() { + if (get_script_instance()) + get_script_instance()->call("update_property"); +} + +void EditorProperty::set_read_only(bool p_read_only) { + read_only = p_read_only; +} + +bool EditorProperty::is_read_only() const { + return read_only; +} + +bool EditorProperty::_might_be_in_instance() { + + if (!object) + return false; + + Node *node = Object::cast_to<Node>(object); + + Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); + + bool might_be = false; + + while (node) { + + if (node->get_scene_instance_state().is_valid()) { + might_be = true; + break; + } + if (node == edited_scene) { + if (node->get_scene_inherited_state().is_valid()) { + might_be = true; + break; + } + might_be = false; + break; + } + node = node->get_owner(); + } + + return might_be; // or might not be +} + +bool EditorProperty::_get_instanced_node_original_property(const StringName &p_prop, Variant &value) { + + Node *node = Object::cast_to<Node>(object); + + if (!node) + return false; + + Node *orig = node; + + Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); + + bool found = false; + + while (node) { + + Ref<SceneState> ss; + + if (node == edited_scene) { + ss = node->get_scene_inherited_state(); + + } else { + ss = node->get_scene_instance_state(); + } + + if (ss.is_valid()) { + + NodePath np = node->get_path_to(orig); + int node_idx = ss->find_node_by_path(np); + if (node_idx >= 0) { + bool lfound = false; + Variant lvar; + lvar = ss->get_property_value(node_idx, p_prop, lfound); + if (lfound) { + + found = true; + value = lvar; + } + } + } + if (node == edited_scene) { + //just in case + break; + } + node = node->get_owner(); + } + + return found; +} + +bool EditorProperty::_is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage) { + + // this is a pretty difficult function, because a property may not be saved but may have + // the flag to not save if one or if zero + + { + Node *node = Object::cast_to<Node>(object); + if (!node) + return false; + + Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); + bool found_state = false; + + while (node) { + + Ref<SceneState> ss; + + if (node == edited_scene) { + ss = node->get_scene_inherited_state(); + + } else { + ss = node->get_scene_instance_state(); + } + + if (ss.is_valid()) { + found_state = true; + } + if (node == edited_scene) { + //just in case + break; + } + node = node->get_owner(); + } + + if (!found_state) + return false; //pointless to check if we are not comparing against anything. + } + + if (p_orig.get_type() == Variant::NIL) { + // not found (was not saved) + // check if it was not saved due to being zero or one + if (p_current.is_zero() && property_usage & PROPERTY_USAGE_STORE_IF_NONZERO) + return false; + if (p_current.is_one() && property_usage & PROPERTY_USAGE_STORE_IF_NONONE) + return false; + } + + if (p_current.get_type() == Variant::REAL && p_orig.get_type() == Variant::REAL) { + float a = p_current; + float b = p_orig; + + return Math::abs(a - b) > CMP_EPSILON; //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error + } + + return bool(Variant::evaluate(Variant::OP_NOT_EQUAL, p_current, p_orig)); +} + +bool EditorProperty::_is_instanced_node_with_original_property_different() { + + bool mbi = _might_be_in_instance(); + if (mbi) { + Variant vorig; + int usage = property_usage & (PROPERTY_USAGE_STORE_IF_NONONE | PROPERTY_USAGE_STORE_IF_NONZERO); + if (_get_instanced_node_original_property(property, vorig) || usage) { + Variant v = object->get(property); + + if (_is_property_different(v, vorig, usage)) { + return true; + } + } + } + return false; +} + +void EditorProperty::update_reload_status() { + + if (property == StringName()) + return; //no property, so nothing to do + + bool has_reload = false; + + if (_is_instanced_node_with_original_property_different()) { + has_reload = true; + } + + if (object->call("property_can_revert", property).operator bool()) { + + has_reload = true; + } + + if (!has_reload && !object->get_script().is_null()) { + Ref<Script> scr = object->get_script(); + Variant orig_value; + if (scr->get_property_default_value(property, orig_value)) { + if (orig_value != object->get(property)) { + has_reload = true; + } + } + } + + if (has_reload != can_revert) { + can_revert = has_reload; + update(); + } +} + +bool EditorProperty::use_keying_next() const { + return false; +} +void EditorProperty::set_checkable(bool p_checkable) { + + checkable = p_checkable; + update(); + queue_sort(); +} + +bool EditorProperty::is_checkable() const { + + return checkable; +} + +void EditorProperty::set_checked(bool p_checked) { + + checked = p_checked; + update(); +} + +bool EditorProperty::is_checked() const { + + return checked; +} + +void EditorProperty::set_draw_red(bool p_draw_red) { + + draw_red = p_draw_red; + update(); +} + +void EditorProperty::set_keying(bool p_keying) { + keying = p_keying; + update(); + queue_sort(); +} + +bool EditorProperty::is_keying() const { + return keying; +} + +bool EditorProperty::is_draw_red() const { + + return draw_red; +} + +void EditorProperty::_focusable_focused(int p_index) { + + if (!selectable) + return; + bool already_selected = selected; + selected = true; + selected_focusable = p_index; + update(); + if (!already_selected && selected) { + emit_signal("selected", property, selected_focusable); + } +} + +void EditorProperty::add_focusable(Control *p_control) { + + p_control->connect("focus_entered", this, "_focusable_focused", varray(focusables.size())); + focusables.push_back(p_control); +} + +void EditorProperty::select(int p_focusable) { + + bool already_selected = selected; + + if (p_focusable >= 0) { + ERR_FAIL_INDEX(p_focusable, focusables.size()); + focusables[p_focusable]->grab_focus(); + } else { + selected = true; + update(); + } + + if (!already_selected && selected) { + emit_signal("selected", property, selected_focusable); + } +} + +void EditorProperty::deselect() { + selected = false; + selected_focusable = -1; + update(); +} + +bool EditorProperty::is_selected() const { + return selected; +} + +void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) { + + if (property == StringName()) + return; + + Ref<InputEventMouse> me = p_event; + + if (me.is_valid()) { + + bool button_left = me->get_button_mask() & BUTTON_MASK_LEFT; + + bool new_keying_hover = keying_rect.has_point(me->get_position()) && !button_left; + if (new_keying_hover != keying_hover) { + keying_hover = new_keying_hover; + update(); + } + + bool new_revert_hover = revert_rect.has_point(me->get_position()) && !button_left; + if (new_revert_hover != revert_hover) { + revert_hover = new_revert_hover; + update(); + } + + bool new_check_hover = check_rect.has_point(me->get_position()) && !button_left; + if (new_check_hover != check_hover) { + check_hover = new_check_hover; + update(); + } + } + + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + + if (!selected && selectable) { + selected = true; + emit_signal("selected", property, -1); + update(); + } + + if (keying_rect.has_point(mb->get_position())) { + emit_signal("property_keyed", property); + } + + if (revert_rect.has_point(mb->get_position())) { + + Variant vorig; + + if (_might_be_in_instance() && _get_instanced_node_original_property(property, vorig)) { + + emit_signal("property_changed", property, vorig.duplicate(true)); + update_property(); + return; + } + + if (object->call("property_can_revert", property).operator bool()) { + Variant rev = object->call("property_get_revert", property); + emit_signal("property_changed", property, rev); + update_property(); + } + + if (!object->get_script().is_null()) { + Ref<Script> scr = object->get_script(); + Variant orig_value; + if (scr->get_property_default_value(property, orig_value)) { + emit_signal("property_changed", property, orig_value); + update_property(); + } + } + } + if (check_rect.has_point(mb->get_position())) { + checked = !checked; + update(); + emit_signal("property_checked", property, checked); + } + } +} + +void EditorProperty::set_label_reference(Control *p_control) { + + label_reference = p_control; +} +void EditorProperty::set_bottom_editor(Control *p_control) { + + bottom_editor = p_control; +} +Variant EditorProperty::get_drag_data(const Point2 &p_point) { + + if (property == StringName()) + return Variant(); + + Dictionary dp; + dp["type"] = "obj_property"; + dp["object"] = object; + dp["property"] = property; + dp["value"] = object->get(property); + + Label *label = memnew(Label); + label->set_text(property); + set_drag_preview(label); + return dp; +} + +void EditorProperty::set_use_folding(bool p_use_folding) { + + use_folding = p_use_folding; +} + +bool EditorProperty::is_using_folding() const { + + return use_folding; +} + +void EditorProperty::expand_all_folding() { +} + +void EditorProperty::collapse_all_folding() { +} + +void EditorProperty::set_selectable(bool p_selectable) { + selectable = p_selectable; +} + +bool EditorProperty::is_selectable() const { + return selectable; +} + +void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) { + object = p_object; + property = p_property; +} + +void EditorProperty::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_label", "text"), &EditorProperty::set_label); + ClassDB::bind_method(D_METHOD("get_label"), &EditorProperty::get_label); + + ClassDB::bind_method(D_METHOD("set_read_only", "read_only"), &EditorProperty::set_read_only); + ClassDB::bind_method(D_METHOD("is_read_only"), &EditorProperty::is_read_only); + + ClassDB::bind_method(D_METHOD("set_checkable", "checkable"), &EditorProperty::set_checkable); + ClassDB::bind_method(D_METHOD("is_checkable"), &EditorProperty::is_checkable); + + ClassDB::bind_method(D_METHOD("set_checked", "checked"), &EditorProperty::set_checked); + ClassDB::bind_method(D_METHOD("is_checked"), &EditorProperty::is_checked); + + ClassDB::bind_method(D_METHOD("set_draw_red", "draw_red"), &EditorProperty::set_draw_red); + ClassDB::bind_method(D_METHOD("is_draw_red"), &EditorProperty::is_draw_red); + + ClassDB::bind_method(D_METHOD("set_keying", "keying"), &EditorProperty::set_keying); + ClassDB::bind_method(D_METHOD("is_keying"), &EditorProperty::is_keying); + + ClassDB::bind_method(D_METHOD("get_edited_property"), &EditorProperty::get_edited_property); + ClassDB::bind_method(D_METHOD("get_edited_object"), &EditorProperty::get_edited_object); + + ClassDB::bind_method(D_METHOD("_gui_input"), &EditorProperty::_gui_input); + ClassDB::bind_method(D_METHOD("_focusable_focused"), &EditorProperty::_focusable_focused); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "label"), "set_label", "get_label"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_only"), "set_read_only", "is_read_only"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "checkable"), "set_checkable", "is_checkable"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "checked"), "set_checked", "is_checked"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_red"), "set_draw_red", "is_draw_red"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keying"), "set_keying", "is_keying"); + ADD_SIGNAL(MethodInfo("property_changed", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); + ADD_SIGNAL(MethodInfo("multiple_properties_changed", PropertyInfo(Variant::POOL_STRING_ARRAY, "properties"), PropertyInfo(Variant::ARRAY, "value"))); + ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property"))); + ADD_SIGNAL(MethodInfo("property_keyed_with_value", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); + ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::STRING, "bool"))); + ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); + ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "focusable_idx"))); + + MethodInfo vm; + vm.name = "update_property"; + BIND_VMETHOD(vm); +} + +EditorProperty::EditorProperty() { + + selectable = true; + text_size = 0; + read_only = false; + checkable = false; + checked = false; + draw_red = false; + keying = false; + keying_hover = false; + revert_hover = false; + check_hover = false; + can_revert = false; + use_folding = false; + property_usage = 0; + selected = false; + selected_focusable = -1; + label_reference = NULL; + bottom_editor = NULL; +} +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +void EditorInspectorPlugin::add_custom_control(Control *control) { + + AddedEditor ae; + ae.property_editor = control; + added_editors.push_back(ae); +} + +void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop) { + + ERR_FAIL_COND(Object::cast_to<EditorProperty>(p_prop) == NULL); + + AddedEditor ae; + ae.properties.push_back(p_for_property); + ae.property_editor = p_prop; + added_editors.push_back(ae); +} + +void EditorInspectorPlugin::add_property_editor_for_multiple_properties(const String &p_label, const Vector<String> &p_properties, Control *p_prop) { + + AddedEditor ae; + ae.properties = p_properties; + ae.property_editor = p_prop; + ae.label = p_label; + added_editors.push_back(ae); +} + +bool EditorInspectorPlugin::can_handle(Object *p_object) { + + if (get_script_instance()) { + return get_script_instance()->call("can_handle", p_object); + } + return false; +} +void EditorInspectorPlugin::parse_begin(Object *p_object) { + + if (get_script_instance()) { + get_script_instance()->call("parse_begin", p_object); + } +} + +void EditorInspectorPlugin::parse_category(Object *p_object, const String &p_parse_category) { + + if (get_script_instance()) { + get_script_instance()->call("parse_category", p_object, p_parse_category); + } +} + +bool EditorInspectorPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) { + + if (get_script_instance()) { + Variant arg[6] = { + p_object, p_type, p_path, p_hint, p_hint_text, p_usage + }; + const Variant *argptr[6] = { + &arg[0], &arg[1], &arg[2], &arg[3], &arg[4], &arg[5] + }; + + Variant::CallError err; + return get_script_instance()->call("parse_property", (const Variant **)&argptr, 6, err); + } + return false; +} +void EditorInspectorPlugin::parse_end() { + + if (get_script_instance()) { + get_script_instance()->call("parse_end"); + } +} + +void EditorInspectorPlugin::_bind_methods() { + + ClassDB::bind_method(D_METHOD("add_custom_control", "control"), &EditorInspectorPlugin::add_custom_control); + ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor"), &EditorInspectorPlugin::add_property_editor); + ClassDB::bind_method(D_METHOD("add_property_editor_for_multiple_properties", "label", "properties", "editor"), &EditorInspectorPlugin::add_property_editor_for_multiple_properties); + + MethodInfo vm; + vm.name = "can_handle"; + vm.arguments.push_back(PropertyInfo(Variant::OBJECT, "object")); + BIND_VMETHOD(vm); + vm.name = "parse_begin"; + BIND_VMETHOD(vm); + vm.name = "parse_category"; + vm.arguments.push_back(PropertyInfo(Variant::STRING, "category")); + BIND_VMETHOD(vm); + vm.arguments.pop_back(); + vm.name = "parse_property"; + vm.return_val.type = Variant::BOOL; + vm.arguments.push_back(PropertyInfo(Variant::INT, "type")); + vm.arguments.push_back(PropertyInfo(Variant::STRING, "path")); + vm.arguments.push_back(PropertyInfo(Variant::INT, "hint")); + vm.arguments.push_back(PropertyInfo(Variant::STRING, "hint_text")); + vm.arguments.push_back(PropertyInfo(Variant::INT, "usage")); + BIND_VMETHOD(vm); + vm.arguments.clear(); + vm.return_val.type = Variant::NIL; + vm.name = "parse_end"; + BIND_VMETHOD(vm); +} + +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +void EditorInspectorCategory::_notification(int p_what) { + + if (p_what == NOTIFICATION_DRAW) { + + draw_rect(Rect2(Vector2(), get_size()), bg_color); + Ref<Font> font = get_font("font", "Tree"); + + int hs = get_constant("hseparation", "Tree"); + + int w = font->get_string_size(label).width; + if (icon.is_valid()) { + w += hs + icon->get_width(); + } + + int ofs = (get_size().width - w) / 2; + + if (icon.is_valid()) { + draw_texture(icon, Point2(ofs, (get_size().height - icon->get_height()) / 2).floor()); + ofs += hs + icon->get_width(); + } + + Color color = get_color("font_color", "Tree"); + draw_string(font, Point2(ofs, font->get_ascent() + (get_size().height - font->get_height()) / 2).floor(), label, color, get_size().width); + } +} + +Size2 EditorInspectorCategory::get_minimum_size() const { + + Ref<Font> font = get_font("font", "Tree"); + + Size2 ms; + ms.width = 1; + ms.height = font->get_height(); + if (icon.is_valid()) { + ms.height = MAX(icon->get_height(), ms.height); + } + ms.height += get_constant("vseparation", "Tree"); + + return ms; +} + +EditorInspectorCategory::EditorInspectorCategory() { +} + +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +void EditorInspectorSection::_notification(int p_what) { + + if (p_what == NOTIFICATION_SORT_CHILDREN) { + + Ref<Font> font = get_font("font", "Tree"); + Ref<Texture> arrow; + +#ifdef TOOLS_ENABLED + if (foldable) { + if (object->editor_is_section_unfolded(section)) { + arrow = get_icon("arrow", "Tree"); + } else { + arrow = get_icon("arrow_collapsed", "Tree"); + } + } +#endif + + Size2 size = get_size(); + Point2 offset; + offset.y = font->get_height(); + if (arrow.is_valid()) { + offset.y = MAX(offset.y, arrow->get_height()); + } + + offset.y += get_constant("vseparation", "Tree"); + offset.x += get_constant("item_margin", "Tree"); + + Rect2 rect(offset, size - offset); + + //set children + for (int i = 0; i < get_child_count(); i++) { + + Control *c = Object::cast_to<Control>(get_child(i)); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + if (!c->is_visible_in_tree()) + continue; + + fit_child_in_rect(c, rect); + } + + update(); //need to redraw text + } + + if (p_what == NOTIFICATION_DRAW) { + + Ref<Texture> arrow; + +#ifdef TOOLS_ENABLED + if (foldable) { + if (object->editor_is_section_unfolded(section)) { + arrow = get_icon("arrow", "Tree"); + } else { + arrow = get_icon("arrow_collapsed", "Tree"); + } + } +#endif + + Ref<Font> font = get_font("font", "Tree"); + + int h = font->get_height(); + if (arrow.is_valid()) { + h = MAX(h, arrow->get_height()); + } + h += get_constant("vseparation", "Tree"); + + draw_rect(Rect2(Vector2(), Vector2(get_size().width, h)), bg_color); + + int hs = get_constant("hseparation", "Tree"); + + int ofs = 0; + if (arrow.is_valid()) { + draw_texture(arrow, Point2(ofs, (h - arrow->get_height()) / 2).floor()); + ofs += hs + arrow->get_width(); + } + + Color color = get_color("font_color", "Tree"); + draw_string(font, Point2(ofs, font->get_ascent() + (h - font->get_height()) / 2).floor(), label, color, get_size().width); + } +} + +Size2 EditorInspectorSection::get_minimum_size() const { + + Size2 ms; + for (int i = 0; i < get_child_count(); i++) { + + Control *c = Object::cast_to<Control>(get_child(i)); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + if (!c->is_visible()) + continue; + Size2 minsize = c->get_combined_minimum_size(); + ms.width = MAX(ms.width, minsize.width); + ms.height = MAX(ms.height, minsize.height); + } + + Ref<Font> font = get_font("font", "Tree"); + ms.height += font->get_ascent() + get_constant("vseparation", "Tree"); + ms.width += get_constant("item_margin", "Tree"); + + return ms; +} + +void EditorInspectorSection::setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable) { + + section = p_section; + label = p_label; + object = p_object; + bg_color = p_bg_color; + foldable = p_foldable; + +#ifdef TOOLS_ENABLED + if (foldable) { + if (object->editor_is_section_unfolded(section)) { + vbox->show(); + } else { + vbox->hide(); + } + } + // void editor_set_section_unfold(const String &p_section, bool p_unfolded); + +#endif +} + +void EditorInspectorSection::_gui_input(const Ref<InputEvent> &p_event) { + + if (!foldable) + return; + +#ifdef TOOLS_ENABLED + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + bool unfold = !object->editor_is_section_unfolded(section); + object->editor_set_section_unfold(section, unfold); + if (unfold) { + vbox->show(); + } else { + vbox->hide(); + } + } +#endif +} + +VBoxContainer *EditorInspectorSection::get_vbox() { + return vbox; +} + +void EditorInspectorSection::unfold() { + + if (!foldable) + return; +#ifdef TOOLS_ENABLED + + object->editor_set_section_unfold(section, true); + vbox->show(); + update(); +#endif +} + +void EditorInspectorSection::fold() { + if (!foldable) + return; + +#ifdef TOOLS_ENABLED + + object->editor_set_section_unfold(section, false); + vbox->hide(); + update(); +#endif +} + +void EditorInspectorSection::_bind_methods() { + + ClassDB::bind_method(D_METHOD("setup", "section", "label", "object", "bg_color", "foldable"), &EditorInspectorSection::setup); + ClassDB::bind_method(D_METHOD("get_vbox"), &EditorInspectorSection::get_vbox); + ClassDB::bind_method(D_METHOD("unfold"), &EditorInspectorSection::unfold); + ClassDB::bind_method(D_METHOD("fold"), &EditorInspectorSection::fold); + ClassDB::bind_method(D_METHOD("_gui_input"), &EditorInspectorSection::_gui_input); +} + +EditorInspectorSection::EditorInspectorSection() { + object = NULL; + foldable = false; + vbox = memnew(VBoxContainer); + add_child(vbox); +} + +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +Ref<EditorInspectorPlugin> EditorInspector::inspector_plugins[MAX_PLUGINS]; +int EditorInspector::inspector_plugin_count = 0; + +void EditorInspector::add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) { + + ERR_FAIL_COND(inspector_plugin_count == MAX_PLUGINS); + + for (int i = 0; i < inspector_plugin_count; i++) { + if (inspector_plugins[i] == p_plugin) + return; //already exists + } + inspector_plugins[inspector_plugin_count++] = p_plugin; +} + +void EditorInspector::remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) { + + ERR_FAIL_COND(inspector_plugin_count == MAX_PLUGINS); + + int idx = -1; + for (int i = 0; i < inspector_plugin_count; i++) { + if (inspector_plugins[i] == p_plugin) { + idx = i; + break; + } + } + + for (int i = idx; i < inspector_plugin_count - 1; i++) { + inspector_plugins[i] = inspector_plugins[i + 1]; + } + inspector_plugin_count--; +} + +void EditorInspector::cleanup_plugins() { + for (int i = 0; i < inspector_plugin_count; i++) { + inspector_plugins[i].unref(); + } + inspector_plugin_count = 0; +} + +void EditorInspector::set_undo_redo(UndoRedo *p_undo_redo) { + undo_redo = p_undo_redo; +} + +String EditorInspector::get_selected_path() const { + + return property_selected; +} + +void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<EditorInspectorPlugin> ped) { + + for (List<EditorInspectorPlugin::AddedEditor>::Element *F = ped->added_editors.front(); F; F = F->next()) { + + EditorProperty *ep = Object::cast_to<EditorProperty>(F->get().property_editor); + current_vbox->add_child(F->get().property_editor); + + if (ep) { + + ep->object = object; + ep->connect("property_changed", this, "_property_changed"); + ep->connect("property_keyed", this, "_property_keyed"); + ep->connect("property_keyed_with_value", this, "_property_keyed_with_value"); + ep->connect("property_checked", this, "_property_checked"); + ep->connect("selected", this, "_property_selected"); + ep->connect("multiple_properties_changed", this, "_multiple_properties_changed"); + ep->connect("resource_selected", this, "_resource_selected", varray(), CONNECT_DEFERRED); + ep->connect("object_id_selected", this, "_object_id_selected", varray(), CONNECT_DEFERRED); + + if (F->get().properties.size()) { + + if (F->get().properties.size() == 1) { + //since it's one, associate: + ep->property = F->get().properties[0]; + ep->property_usage = 0; + } + + if (F->get().label != String()) { + ep->set_label(F->get().label); + } + + for (int i = 0; i < F->get().properties.size(); i++) { + String prop = F->get().properties[i]; + + if (!editor_property_map.has(prop)) { + editor_property_map[prop] = List<EditorProperty *>(); + } + editor_property_map[prop].push_back(ep); + } + } + + ep->set_read_only(read_only); + ep->update_property(); + ep->update_reload_status(); + } + } + ped->added_editors.clear(); +} + +void EditorInspector::update_tree() { + + //to update properly if all is refreshed + StringName current_selected = property_selected; + int current_focusable = property_focusable; + + _clear(); + + if (!object) + return; + + List<Ref<EditorInspectorPlugin> > valid_plugins; + + for (int i = inspector_plugin_count - 1; i >= 0; i--) { //start by last, so lastly added can override newly added + if (!inspector_plugins[i]->can_handle(object)) + continue; + valid_plugins.push_back(inspector_plugins[i]); + } + + bool draw_red = false; + + { + Node *nod = Object::cast_to<Node>(object); + Node *es = EditorNode::get_singleton()->get_edited_scene(); + if (nod && es != nod && nod->get_owner() != es) { + draw_red = true; + } + } + + // TreeItem *current_category = NULL; + + String filter = search_box ? search_box->get_text() : ""; + String group; + String group_base; + + List<PropertyInfo> plist; + object->get_property_list(&plist, true); + + HashMap<String, VBoxContainer *> item_path; + item_path[""] = main_vbox; + + Color sscolor = get_color("prop_subsection", "Editor"); + + for (List<Ref<EditorInspectorPlugin> >::Element *E = valid_plugins.front(); E; E = E->next()) { + Ref<EditorInspectorPlugin> ped = E->get(); + ped->parse_begin(object); + _parse_added_editors(main_vbox, ped); + } + + for (List<PropertyInfo>::Element *I = plist.front(); I; I = I->next()) { + + PropertyInfo &p = I->get(); + + //make sure the property can be edited + + if (p.usage & PROPERTY_USAGE_GROUP) { + + group = p.name; + group_base = p.hint_string; + + continue; + + } else if (p.usage & PROPERTY_USAGE_CATEGORY) { + + group = ""; + group_base = ""; + + if (!show_categories) + continue; + + List<PropertyInfo>::Element *N = I->next(); + bool valid = true; + //if no properties in category, skip + while (N) { + if (N->get().usage & PROPERTY_USAGE_EDITOR) + break; + if (N->get().usage & PROPERTY_USAGE_CATEGORY) { + valid = false; + break; + } + N = N->next(); + } + if (!valid) + continue; //empty, ignore + + EditorInspectorCategory *category = memnew(EditorInspectorCategory); + main_vbox->add_child(category); + + String type = p.name; + if (has_icon(type, "EditorIcons")) + category->icon = get_icon(type, "EditorIcons"); + else + category->icon = get_icon("Object", "EditorIcons"); + category->label = type; + + category->bg_color = get_color("prop_category", "Editor"); + if (use_doc_hints) { + StringName type = p.name; + if (!class_descr_cache.has(type)) { + + String descr; + DocData *dd = EditorHelp::get_doc_data(); + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(type); + if (E) { + descr = E->get().brief_description; + } + class_descr_cache[type] = descr.word_wrap(80); + } + + category->set_tooltip(TTR("Class:") + " " + p.name + (class_descr_cache[type] == "" ? "" : "\n\n" + class_descr_cache[type])); + } + + for (List<Ref<EditorInspectorPlugin> >::Element *E = valid_plugins.front(); E; E = E->next()) { + Ref<EditorInspectorPlugin> ped = E->get(); + ped->parse_category(object, p.name); + _parse_added_editors(main_vbox, ped); + } + + continue; + + } else if (!(p.usage & PROPERTY_USAGE_EDITOR)) + continue; + + if (hide_script && p.name == "script") + continue; + + String basename = p.name; + if (group != "") { + if (group_base != "") { + if (basename.begins_with(group_base)) { + basename = basename.replace_first(group_base, ""); + } else if (group_base.begins_with(basename)) { + //keep it, this is used pretty often + } else { + group = ""; //no longer using group base, clear + } + } + } + + if (group != "") { + basename = group + "/" + basename; + } + + String name = (basename.find("/") != -1) ? basename.right(basename.find_last("/") + 1) : basename; + + if (capitalize_paths) { + int dot = name.find("."); + if (dot != -1) { + String ov = name.right(dot); + name = name.substr(0, dot); + name = name.camelcase_to_underscore().capitalize(); + name += ov; + + } else { + name = name.camelcase_to_underscore().capitalize(); + } + } + + String path = basename.left(basename.find_last("/")); + + if (use_filter && filter != "") { + + String cat = path; + + if (capitalize_paths) + cat = cat.capitalize(); + + if (!filter.is_subsequence_ofi(cat) && !filter.is_subsequence_ofi(name)) + continue; + } + + VBoxContainer *current_vbox = main_vbox; + + { + + String acc_path = ""; + int level = 1; + for (int i = 0; i < path.get_slice_count("/"); i++) { + String path_name = path.get_slice("/", i); + if (i > 0) + acc_path += "/"; + acc_path += path_name; + if (!item_path.has(acc_path)) { + EditorInspectorSection *section = memnew(EditorInspectorSection); + current_vbox->add_child(section); + sections.push_back(section); + + if (capitalize_paths) + path_name = path_name.capitalize(); + Color c = sscolor; + c.a /= level; + section->setup(path_name, acc_path, object, c, use_folding); + + item_path[acc_path] = section->get_vbox(); + } + current_vbox = item_path[acc_path]; + level = (MIN(level + 1, 4)); + } + } + + bool checkable = false; + bool checked = false; + if (p.usage & PROPERTY_USAGE_CHECKABLE) { + checkable = true; + checked = p.usage & PROPERTY_USAGE_CHECKED; + } + + String doc_hint; + + if (use_doc_hints) { + + StringName classname = object->get_class_name(); + StringName propname = p.name; + String descr; + bool found = false; + + Map<StringName, Map<StringName, String> >::Element *E = descr_cache.find(classname); + if (E) { + Map<StringName, String>::Element *F = E->get().find(propname); + if (F) { + found = true; + descr = F->get(); + } + } + + if (!found) { + DocData *dd = EditorHelp::get_doc_data(); + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(classname); + while (E && descr == String()) { + for (int i = 0; i < E->get().properties.size(); i++) { + if (E->get().properties[i].name == propname.operator String()) { + descr = E->get().properties[i].description.strip_edges().word_wrap(80); + break; + } + } + if (!E->get().inherits.empty()) { + E = dd->class_list.find(E->get().inherits); + } else { + break; + } + } + descr_cache[classname][propname] = descr; + } + + doc_hint = descr; + } + +#if 0 + if (p.name == selected_property) { + + item->select(1); + } +#endif + for (List<Ref<EditorInspectorPlugin> >::Element *E = valid_plugins.front(); E; E = E->next()) { + Ref<EditorInspectorPlugin> ped = E->get(); + ped->parse_property(object, p.type, p.name, p.hint, p.hint_string, p.usage); + List<EditorInspectorPlugin::AddedEditor> editors = ped->added_editors; //make a copy, since plugins may be used again in a sub-inspector + ped->added_editors.clear(); + + for (List<EditorInspectorPlugin::AddedEditor>::Element *F = editors.front(); F; F = F->next()) { + + EditorProperty *ep = Object::cast_to<EditorProperty>(F->get().property_editor); + current_vbox->add_child(F->get().property_editor); + + if (ep) { + + ep->object = object; + ep->connect("property_changed", this, "_property_changed"); + ep->connect("property_keyed", this, "_property_keyed"); + ep->connect("property_keyed_with_value", this, "_property_keyed_with_value"); + ep->connect("property_checked", this, "_property_checked"); + ep->connect("selected", this, "_property_selected"); + ep->connect("multiple_properties_changed", this, "_multiple_properties_changed"); + ep->connect("resource_selected", this, "_resource_selected", varray(), CONNECT_DEFERRED); + ep->connect("object_id_selected", this, "_object_id_selected", varray(), CONNECT_DEFERRED); + if (doc_hint != String()) { + ep->set_tooltip(TTR("Property: ") + p.name + "\n\n" + doc_hint); + } else { + ep->set_tooltip(TTR("Property: ") + p.name); + } + ep->set_draw_red(draw_red); + ep->set_use_folding(use_folding); + ep->set_checkable(checkable); + ep->set_checked(checked); + ep->set_keying(keying); + + if (F->get().properties.size()) { + + if (F->get().properties.size() == 1) { + //since it's one, associate: + ep->property = F->get().properties[0]; + ep->property_usage = p.usage; + //and set label? + } + + if (F->get().label != String()) { + ep->set_label(F->get().label); + } else { + //use existin one + ep->set_label(name); + } + for (int i = 0; i < F->get().properties.size(); i++) { + String prop = F->get().properties[i]; + + if (!editor_property_map.has(prop)) { + editor_property_map[prop] = List<EditorProperty *>(); + } + editor_property_map[prop].push_back(ep); + } + } + + ep->set_read_only(read_only); + ep->update_property(); + ep->update_reload_status(); + + if (current_selected && ep->property == current_selected) { + ep->select(current_focusable); + } + } + } + } + } + + for (List<Ref<EditorInspectorPlugin> >::Element *E = valid_plugins.front(); E; E = E->next()) { + Ref<EditorInspectorPlugin> ped = E->get(); + ped->parse_end(); + _parse_added_editors(main_vbox, ped); + } + + //see if this property exists and should be kept +} +void EditorInspector::update_property(const String &p_prop) { + if (!editor_property_map.has(p_prop)) + return; + + for (List<EditorProperty *>::Element *E = editor_property_map[p_prop].front(); E; E = E->next()) { + E->get()->update_property(); + E->get()->update_reload_status(); + } +} + +void EditorInspector::_clear() { + + while (main_vbox->get_child_count()) { + memdelete(main_vbox->get_child(0)); + } + property_selected = StringName(); + property_focusable = -1; + editor_property_map.clear(); + sections.clear(); + pending.clear(); +} + +void EditorInspector::refresh() { + + if (refresh_countdown > 0) + return; + refresh_countdown = EditorSettings::get_singleton()->get("docks/property_editor/auto_refresh_interval"); +} + +Object *EditorInspector::get_edited_object() { + return object; +} + +void EditorInspector::edit(Object *p_object) { + if (object == p_object) + return; + if (object) { + + _clear(); + object->remove_change_receptor(this); + } + + object = p_object; + + if (object) { + object->add_change_receptor(this); + update_tree(); + } +} + +void EditorInspector::set_keying(bool p_active) { + if (keying == p_active) + return; + keying = p_active; + update_tree(); +} +void EditorInspector::set_read_only(bool p_read_only) { + read_only = p_read_only; + update_tree(); +} + +bool EditorInspector::is_capitalize_paths_enabled() const { + + return capitalize_paths; +} +void EditorInspector::set_enable_capitalize_paths(bool p_capitalize) { + capitalize_paths = p_capitalize; + update_tree(); +} + +void EditorInspector::set_autoclear(bool p_enable) { + autoclear = p_enable; +} + +void EditorInspector::set_show_categories(bool p_show) { + show_categories = p_show; + update_tree(); +} + +void EditorInspector::set_use_doc_hints(bool p_enable) { + use_doc_hints = p_enable; + update_tree(); +} +void EditorInspector::set_hide_script(bool p_hide) { + hide_script = p_hide; + update_tree(); +} +void EditorInspector::set_use_filter(bool p_use) { + use_filter = p_use; + update_tree(); +} +void EditorInspector::register_text_enter(Node *p_line_edit) { + search_box = Object::cast_to<LineEdit>(p_line_edit); + if (search_box) + search_box->connect("text_changed", this, "_filter_changed"); +} + +void EditorInspector::_filter_changed(const String &p_text) { + + update_tree(); +} + +void EditorInspector::set_subsection_selectable(bool p_selectable) { +} + +void EditorInspector::set_property_selectable(bool p_selectable) { +} + +void EditorInspector::set_use_folding(bool p_enable) { + use_folding = p_enable; + update_tree(); +} + +void EditorInspector::collapse_all_folding() { + + for (List<EditorInspectorSection *>::Element *E = sections.front(); E; E = E->next()) { + E->get()->fold(); + } + + for (Map<StringName, List<EditorProperty *> >::Element *F = editor_property_map.front(); F; F = F->next()) { + for (List<EditorProperty *>::Element *E = F->get().front(); E; E = E->next()) { + E->get()->collapse_all_folding(); + } + } +} + +void EditorInspector::expand_all_folding() { + for (List<EditorInspectorSection *>::Element *E = sections.front(); E; E = E->next()) { + E->get()->unfold(); + } + for (Map<StringName, List<EditorProperty *> >::Element *F = editor_property_map.front(); F; F = F->next()) { + for (List<EditorProperty *>::Element *E = F->get().front(); E; E = E->next()) { + E->get()->expand_all_folding(); + } + } +} + +void EditorInspector::set_scroll_offset(int p_offset) { + set_v_scroll(p_offset); +} + +int EditorInspector::get_scroll_offset() const { + return get_v_scroll(); +} + +void EditorInspector::_edit_request_change(Object *p_object, const String &p_property) { + + if (object != p_object) //may be undoing/redoing for a non edited object, so ignore + return; + + if (changing) + return; + + if (p_property == String()) + update_tree_pending = true; + else { + pending.insert(p_property); + } +} + +void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all, const String &p_changed_field) { + + if (autoclear && editor_property_map.has(p_name)) { + for (List<EditorProperty *>::Element *E = editor_property_map[p_name].front(); E; E = E->next()) { + if (E->get()->is_checkable()) { + E->get()->set_checked(true); + } + } + } + + if (!undo_redo || Object::cast_to<ArrayPropertyEdit>(object) || Object::cast_to<DictionaryPropertyEdit>(object)) { //kind of hacky + + object->set(p_name, p_value); + if (p_refresh_all) + _edit_request_change(object, ""); + else + _edit_request_change(object, p_name); + + emit_signal(_prop_edited, p_name); + + } else if (Object::cast_to<MultiNodeEdit>(object)) { + + Object::cast_to<MultiNodeEdit>(object)->set_property_field(p_name, p_value, p_changed_field); + _edit_request_change(object, p_name); + emit_signal(_prop_edited, p_name); + } else { + + undo_redo->create_action(TTR("Set") + " " + p_name, UndoRedo::MERGE_ENDS); + undo_redo->add_do_property(object, p_name, p_value); + undo_redo->add_undo_property(object, p_name, object->get(p_name)); + + if (p_refresh_all) { + undo_redo->add_do_method(this, "_edit_request_change", object, ""); + undo_redo->add_undo_method(this, "_edit_request_change", object, ""); + } else { + + undo_redo->add_do_method(this, "_edit_request_change", object, p_name); + undo_redo->add_undo_method(this, "_edit_request_change", object, p_name); + } + + Resource *r = Object::cast_to<Resource>(object); + if (r) { + if (!r->is_edited() && String(p_name) != "resource/edited") { + undo_redo->add_do_method(r, "set_edited", true); + undo_redo->add_undo_method(r, "set_edited", false); + } + + if (String(p_name) == "resource_local_to_scene") { + bool prev = object->get(p_name); + bool next = p_value; + if (next) { + undo_redo->add_do_method(r, "setup_local_to_scene"); + } + if (prev) { + undo_redo->add_undo_method(r, "setup_local_to_scene"); + } + } + } + undo_redo->add_do_method(this, "emit_signal", _prop_edited, p_name); + undo_redo->add_undo_method(this, "emit_signal", _prop_edited, p_name); + changing++; + undo_redo->commit_action(); + changing--; + } + + if (editor_property_map.has(p_name)) { + for (List<EditorProperty *>::Element *E = editor_property_map[p_name].front(); E; E = E->next()) { + E->get()->update_reload_status(); + } + } +} + +void EditorInspector::_property_changed(const String &p_path, const Variant &p_value) { + + _edit_set(p_path, p_value, false, ""); +} + +void EditorInspector::_multiple_properties_changed(Vector<String> p_paths, Array p_values) { + + ERR_FAIL_COND(p_paths.size() == 0 || p_values.size() == 0); + ERR_FAIL_COND(p_paths.size() != p_values.size()); + String names; + for (int i = 0; i < p_paths.size(); i++) { + if (i > 0) + names += ","; + names += p_paths[i]; + } + undo_redo->create_action(TTR("Set Multiple:") + " " + names, UndoRedo::MERGE_ENDS); + for (int i = 0; i < p_paths.size(); i++) { + _edit_set(p_paths[i], p_values[i], false, ""); + } + changing++; + undo_redo->commit_action(); + changing--; +} + +void EditorInspector::_property_keyed(const String &p_path) { + + if (!object) + return; + + emit_signal("property_keyed", p_path, object->get(p_path), false); //second param is deprecated +} + +void EditorInspector::_property_keyed_with_value(const String &p_path, const Variant &p_value) { + + if (!object) + return; + + emit_signal("property_keyed", p_path, p_value, false); //second param is deprecated +} + +void EditorInspector::_property_checked(const String &p_path, bool p_checked) { + + if (!object) + return; + + //property checked + if (autoclear) { + + if (!p_checked) { + object->set(p_path, Variant()); + } else { + + Variant to_create; + List<PropertyInfo> pinfo; + object->get_property_list(&pinfo); + for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { + if (E->get().name == p_path) { + Variant::CallError ce; + to_create = Variant::construct(E->get().type, NULL, 0, ce); + break; + } + } + object->set(p_path, to_create); + } + + if (editor_property_map.has(p_path)) { + for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) { + E->get()->update_property(); + E->get()->update_reload_status(); + } + } + + } else { + emit_signal("property_toggled", p_path, p_checked); + } +} + +void EditorInspector::_property_selected(const String &p_path, int p_focusable) { + + property_selected = p_path; + property_focusable = p_focusable; + //deselect the others + for (Map<StringName, List<EditorProperty *> >::Element *F = editor_property_map.front(); F; F = F->next()) { + if (F->key() == property_selected) + continue; + for (List<EditorProperty *>::Element *E = F->get().front(); E; E = E->next()) { + if (E->get()->is_selected()) + E->get()->deselect(); + } + } +} + +void EditorInspector::_object_id_selected(const String &p_path, ObjectID p_id) { + + emit_signal("object_id_selected", p_id); +} + +void EditorInspector::_resource_selected(const String &p_path, RES p_resource) { + emit_signal("resource_selected", p_resource, p_path); +} + +void EditorInspector::_node_removed(Node *p_node) { + + if (p_node == object) { + edit(NULL); + } +} + +void EditorInspector::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + get_tree()->connect("node_removed", this, "_node_removed"); + add_style_override("bg", get_stylebox("bg", "Tree")); + } + if (p_what == NOTIFICATION_EXIT_TREE) { + + get_tree()->disconnect("node_removed", this, "_node_removed"); + edit(NULL); + } + + if (p_what == NOTIFICATION_PROCESS) { + + if (refresh_countdown > 0) { + refresh_countdown -= get_process_delta_time(); + if (refresh_countdown <= 0) { + for (Map<StringName, List<EditorProperty *> >::Element *F = editor_property_map.front(); F; F = F->next()) { + for (List<EditorProperty *>::Element *E = F->get().front(); E; E = E->next()) { + E->get()->update_property(); + E->get()->update_reload_status(); + } + } + } + } + + changing++; + + if (update_tree_pending) { + + update_tree(); + update_tree_pending = false; + pending.clear(); + + } else { + + while (pending.size()) { + StringName prop = pending.front()->get(); + if (editor_property_map.has(prop)) { + for (List<EditorProperty *>::Element *E = editor_property_map[prop].front(); E; E = E->next()) { + E->get()->update_property(); + E->get()->update_reload_status(); + } + } + pending.erase(pending.front()); + } + } + + changing--; + } + + if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { + update_tree(); + } +} + +void EditorInspector::_changed_callback(Object *p_changed, const char *p_prop) { + //this is called when property change is notified via _change_notify() + _edit_request_change(p_changed, p_prop); +} + +void EditorInspector::_bind_methods() { + + ClassDB::bind_method("_multiple_properties_changed", &EditorInspector::_multiple_properties_changed); + ClassDB::bind_method("_property_changed", &EditorInspector::_property_changed); + ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change); + ClassDB::bind_method("_node_removed", &EditorInspector::_node_removed); + ClassDB::bind_method("_filter_changed", &EditorInspector::_filter_changed); + ClassDB::bind_method("_property_keyed", &EditorInspector::_property_keyed); + ClassDB::bind_method("_property_keyed_with_value", &EditorInspector::_property_keyed_with_value); + ClassDB::bind_method("_property_checked", &EditorInspector::_property_checked); + ClassDB::bind_method("_property_selected", &EditorInspector::_property_selected); + ClassDB::bind_method("_resource_selected", &EditorInspector::_resource_selected); + ClassDB::bind_method("_object_id_selected", &EditorInspector::_object_id_selected); + + ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property"))); + ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop"))); + ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id"))); +} + +EditorInspector::EditorInspector() { + object = NULL; + undo_redo = NULL; + main_vbox = memnew(VBoxContainer); + main_vbox->set_h_size_flags(SIZE_EXPAND_FILL); + add_child(main_vbox); + set_enable_h_scroll(false); + set_enable_v_scroll(true); + + show_categories = false; + hide_script = true; + use_doc_hints = false; + capitalize_paths = false; + use_filter = false; + autoclear = false; + changing = 0; + use_folding = false; + update_all_pending = false; + update_tree_pending = false; + refresh_countdown = 0; + read_only = false; + search_box = NULL; + keying = false; + _prop_edited = "property_edited"; + set_process(true); + property_focusable = -1; +} diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h new file mode 100644 index 0000000000..a6b183799f --- /dev/null +++ b/editor/editor_inspector.h @@ -0,0 +1,327 @@ +/*************************************************************************/ +/* editor_inspector.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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_INSPECTOR_H +#define EDITOR_INSPECTOR_H + +#include "scene/gui/box_container.h" +#include "scene/gui/line_edit.h" +#include "scene/gui/scroll_container.h" + +class UndoRedo; + +class EditorProperty : public Container { + + GDCLASS(EditorProperty, Container) +private: + String label; + int text_size; + friend class EditorInspector; + Object *object; + StringName property; + + int property_usage; + + bool read_only; + bool checkable; + bool checked; + bool draw_red; + bool keying; + + Rect2 keying_rect; + bool keying_hover; + Rect2 revert_rect; + bool revert_hover; + Rect2 check_rect; + bool check_hover; + + bool can_revert; + + bool use_folding; + + bool _might_be_in_instance(); + bool _is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage); + bool _is_instanced_node_with_original_property_different(); + bool _get_instanced_node_original_property(const StringName &p_prop, Variant &value); + void _focusable_focused(int p_index); + + bool selectable; + bool selected; + int selected_focusable; + + Vector<Control *> focusables; + Control *label_reference; + Control *bottom_editor; + +protected: + void _notification(int p_what); + static void _bind_methods(); + + void _gui_input(const Ref<InputEvent> &p_event); + +public: + virtual Size2 get_minimum_size() const; + + void set_label(const String &p_label); + String get_label() const; + + void set_read_only(bool p_read_only); + bool is_read_only() const; + + Object *get_edited_object(); + StringName get_edited_property(); + + virtual void update_property(); + void update_reload_status(); + + virtual bool use_keying_next() const; + + void set_checkable(bool p_checkable); + bool is_checkable() const; + + void set_checked(bool p_checked); + bool is_checked() const; + + void set_draw_red(bool p_draw_red); + bool is_draw_red() const; + + void set_keying(bool p_keying); + bool is_keying() const; + + void add_focusable(Control *p_control); + void select(int p_focusable = -1); + void deselect(); + bool is_selected() const; + + void set_label_reference(Control *p_control); + void set_bottom_editor(Control *p_editor); + + void set_use_folding(bool p_use_folding); + bool is_using_folding() const; + + virtual void expand_all_folding(); + virtual void collapse_all_folding(); + + virtual Variant get_drag_data(const Point2 &p_point); + + void set_selectable(bool p_selectable); + bool is_selectable() const; + + void set_object_and_property(Object *p_object, const StringName &p_property); + EditorProperty(); +}; + +class EditorInspectorPlugin : public Reference { + GDCLASS(EditorInspectorPlugin, Reference) + + friend class EditorInspector; + struct AddedEditor { + Control *property_editor; + Vector<String> properties; + String label; + }; + + List<AddedEditor> added_editors; + +protected: + static void _bind_methods(); + +public: + void add_custom_control(Control *control); + void add_property_editor(const String &p_for_property, Control *p_prop); + void add_property_editor_for_multiple_properties(const String &p_label, const Vector<String> &p_properties, Control *p_prop); + + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); + virtual void parse_category(Object *p_object, const String &p_parse_category); + virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage); + virtual void parse_end(); +}; + +class EditorInspectorCategory : public Control { + GDCLASS(EditorInspectorCategory, Control); + + friend class EditorInspector; + Ref<Texture> icon; + String label; + Color bg_color; + +protected: + void _notification(int p_what); + +public: + virtual Size2 get_minimum_size() const; + + EditorInspectorCategory(); +}; + +class EditorInspectorSection : public Container { + GDCLASS(EditorInspectorSection, Container); + + String label; + String section; + Object *object; + VBoxContainer *vbox; + Color bg_color; + bool foldable; + +protected: + void _notification(int p_what); + static void _bind_methods(); + void _gui_input(const Ref<InputEvent> &p_event); + +public: + virtual Size2 get_minimum_size() const; + + void setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable); + VBoxContainer *get_vbox(); + void unfold(); + void fold(); + + Object *get_edited_object(); + + EditorInspectorSection(); +}; + +class EditorInspector : public ScrollContainer { + GDCLASS(EditorInspector, ScrollContainer); + + UndoRedo *undo_redo; + enum { + MAX_PLUGINS = 1024 + }; + static Ref<EditorInspectorPlugin> inspector_plugins[MAX_PLUGINS]; + static int inspector_plugin_count; + + VBoxContainer *main_vbox; + + //map use to cache the instanced editors + Map<StringName, List<EditorProperty *> > editor_property_map; + List<EditorInspectorSection *> sections; + Set<StringName> pending; + + void _clear(); + Object *object; + + // + + LineEdit *search_box; + bool show_categories; + bool hide_script; + bool use_doc_hints; + bool capitalize_paths; + bool use_filter; + bool autoclear; + bool use_folding; + int changing; + bool update_all_pending; + bool read_only; + bool keying; + + int refresh_countdown; + bool update_tree_pending; + StringName _prop_edited; + StringName property_selected; + int property_focusable; + + Map<StringName, Map<StringName, String> > descr_cache; + Map<StringName, String> class_descr_cache; + + void _edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all, const String &p_changed_field); + + void _property_changed(const String &p_path, const Variant &p_value); + void _multiple_properties_changed(Vector<String> p_paths, Array p_values); + void _property_keyed(const String &p_path); + void _property_keyed_with_value(const String &p_path, const Variant &p_value); + + void _property_checked(const String &p_path, bool p_checked); + + void _resource_selected(const String &p_path, RES p_resource); + void _property_selected(const String &p_path, int p_focusable); + void _object_id_selected(const String &p_path, ObjectID p_id); + + void _node_removed(Node *p_node); + + void _changed_callback(Object *p_changed, const char *p_prop); + void _edit_request_change(Object *p_changed, const String &p_prop); + + void _filter_changed(const String &p_text); + void _parse_added_editors(VBoxContainer *current_vbox, Ref<EditorInspectorPlugin> ped); + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + static void add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin); + static void remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin); + static void cleanup_plugins(); + + void set_undo_redo(UndoRedo *p_undo_redo); + + String get_selected_path() const; + + void update_tree(); + void update_property(const String &p_prop); + + void refresh(); + + void edit(Object *p_object); + Object *get_edited_object(); + + void set_keying(bool p_active); + void set_read_only(bool p_read_only); + + bool is_capitalize_paths_enabled() const; + void set_enable_capitalize_paths(bool p_capitalize); + void set_autoclear(bool p_enable); + + void set_show_categories(bool p_show); + void set_use_doc_hints(bool p_enable); + void set_hide_script(bool p_hide); + + void set_use_filter(bool p_use); + void register_text_enter(Node *p_line_edit); + + void set_subsection_selectable(bool p_selectable); + void set_property_selectable(bool p_selectable); + + void set_use_folding(bool p_enable); + + void collapse_all_folding(); + void expand_all_folding(); + + void set_scroll_offset(int p_offset); + int get_scroll_offset() const; + + EditorInspector(); +}; + +#endif // INSPECTOR_H diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index fd5a6dffc9..4b068f1000 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -56,6 +56,7 @@ #include "editor/editor_file_system.h" #include "editor/editor_help.h" #include "editor/editor_initialize_ssl.h" +#include "editor/editor_properties.h" #include "editor/editor_settings.h" #include "editor/editor_themes.h" #include "editor/import/editor_import_collada.h" @@ -294,7 +295,6 @@ void EditorNode::_notification(int p_what) { get_tree()->get_root()->set_as_audio_listener_2d(false); get_tree()->set_auto_accept_quit(false); get_tree()->connect("files_dropped", this, "_dropped_files"); - property_editable_warning->set_icon(gui_base->get_icon("NodeWarning", "EditorIcons")); } if (p_what == NOTIFICATION_EXIT_TREE) { @@ -323,7 +323,7 @@ void EditorNode::_notification(int p_what) { } if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { - scene_tabs->set_tab_close_display_policy((bool(EDITOR_DEF("interface/scene_tabs/always_show_close_button", false)) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY)); + scene_tabs->set_tab_close_display_policy((bool(EDITOR_GET("interface/scene_tabs/always_show_close_button")) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY)); Ref<Theme> theme = create_editor_theme(theme_base->get_theme()); theme_base->set_theme(theme); @@ -342,8 +342,8 @@ void EditorNode::_notification(int p_what) { settings_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles")); help_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles")); - if (bool(EDITOR_DEF("interface/scene_tabs/resize_if_many_tabs", true))) { - scene_tabs->set_min_width(int(EDITOR_DEF("interface/scene_tabs/minimum_width", 50)) * EDSCALE); + if (EDITOR_GET("interface/scene_tabs/resize_if_many_tabs")) { + scene_tabs->set_min_width(int(EDITOR_GET("interface/scene_tabs/minimum_width")) * EDSCALE); } else { scene_tabs->set_min_width(0); } @@ -381,16 +381,6 @@ void EditorNode::_notification(int p_what) { distraction_free->set_icon(gui_base->get_icon("DistractionFree", "EditorIcons")); scene_tab_add->set_icon(gui_base->get_icon("Add", "EditorIcons")); - resource_new_button->set_icon(gui_base->get_icon("New", "EditorIcons")); - resource_load_button->set_icon(gui_base->get_icon("Load", "EditorIcons")); - resource_save_button->set_icon(gui_base->get_icon("Save", "EditorIcons")); - - property_back->set_icon(gui_base->get_icon("Back", "EditorIcons")); - property_forward->set_icon(gui_base->get_icon("Forward", "EditorIcons")); - editor_history_menu->set_icon(gui_base->get_icon("History", "EditorIcons")); - - search_button->set_icon(gui_base->get_icon("Search", "EditorIcons")); - object_menu->set_icon(gui_base->get_icon("Tools", "EditorIcons")); // clear_button->set_icon(gui_base->get_icon("Close", "EditorIcons")); don't have access to that node. needs to become a class property update_menu->set_icon(gui_base->get_icon("Collapse", "EditorIcons")); dock_tab_move_left->set_icon(theme->get_icon("Back", "EditorIcons")); @@ -483,7 +473,7 @@ void EditorNode::_fs_changed() { // come during the export export_defer.preset = ""; Error err = OK; - if (!preset->is_runnable() && (export_defer.path.ends_with(".pck") || export_defer.path.ends_with(".zip"))) { + if (export_defer.path.ends_with(".pck") || export_defer.path.ends_with(".zip")) { if (export_defer.path.ends_with(".zip")) { err = platform->export_zip(preset, export_defer.debug, export_defer.path); } else if (export_defer.path.ends_with(".pck")) { @@ -548,8 +538,8 @@ void EditorNode::_vp_resized() { void EditorNode::_node_renamed() { - if (property_editor) - property_editor->update_tree(); + if (get_inspector()) + get_inspector()->update_tree(); } void EditorNode::_editor_select_next() { @@ -581,38 +571,16 @@ Error EditorNode::load_resource(const String &p_scene) { RES res = ResourceLoader::load(p_scene); ERR_FAIL_COND_V(!res.is_valid(), ERR_CANT_OPEN); - edit_resource(res); + inspector_dock->edit_resource(res); return OK; } -void EditorNode::edit_resource(const Ref<Resource> &p_resource) { - - _resource_selected(p_resource, ""); -} - void EditorNode::edit_node(Node *p_node) { push_item(p_node); } -void EditorNode::open_resource(const String &p_type) { - - file->set_mode(EditorFileDialog::MODE_OPEN_FILE); - - List<String> extensions; - ResourceLoader::get_recognized_extensions_for_type(p_type, &extensions); - - file->clear_filters(); - for (int i = 0; i < extensions.size(); i++) { - - file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); - } - - file->popup_centered_ratio(); - current_option = RESOURCE_LOAD; -} - void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path) { editor_data.apply_changes_in_editors(); @@ -1144,20 +1112,6 @@ void EditorNode::_mark_unsaved_scenes() { void EditorNode::_dialog_action(String p_file) { switch (current_option) { - - case RESOURCE_LOAD: { - - RES res = ResourceLoader::load(p_file); - if (res.is_null()) { - - current_option = -1; - accept->get_ok()->set_text("Ugh"); - accept->set_text(TTR("Failed to load resource.")); - return; - }; - - push_item(res.operator->()); - } break; case FILE_NEW_INHERITED_SCENE: { load_scene(p_file, false, true); @@ -1351,7 +1305,7 @@ void EditorNode::_dialog_action(String p_file) { void EditorNode::push_item(Object *p_object, const String &p_property) { if (!p_object) { - property_editor->edit(NULL); + get_inspector()->edit(NULL); node_dock->set_node(NULL); scene_tree_dock->set_selected(NULL); return; @@ -1369,89 +1323,6 @@ void EditorNode::push_item(Object *p_object, const String &p_property) { _edit_current(); } -void EditorNode::_select_history(int p_idx) { - - //push it to the top, it is not correct, but it's more useful - ObjectID id = editor_history.get_history_obj(p_idx); - Object *obj = ObjectDB::get_instance(id); - if (!obj) - return; - push_item(obj); -} - -void EditorNode::_prepare_history() { - - int history_to = MAX(0, editor_history.get_history_len() - 25); - - editor_history_menu->get_popup()->clear(); - - Ref<Texture> base_icon = gui_base->get_icon("Object", "EditorIcons"); - Set<ObjectID> already; - for (int i = editor_history.get_history_len() - 1; i >= history_to; i--) { - - ObjectID id = editor_history.get_history_obj(i); - Object *obj = ObjectDB::get_instance(id); - if (!obj || already.has(id)) { - if (history_to > 0) { - history_to--; - } - continue; - } - - already.insert(id); - - Ref<Texture> icon = gui_base->get_icon("Object", "EditorIcons"); - if (gui_base->has_icon(obj->get_class(), "EditorIcons")) - icon = gui_base->get_icon(obj->get_class(), "EditorIcons"); - else - icon = base_icon; - - String text; - if (Object::cast_to<Resource>(obj)) { - Resource *r = Object::cast_to<Resource>(obj); - if (r->get_path().is_resource_file()) - text = r->get_path().get_file(); - else if (r->get_name() != String()) { - text = r->get_name(); - } else { - text = r->get_class(); - } - } else if (Object::cast_to<Node>(obj)) { - text = Object::cast_to<Node>(obj)->get_name(); - } else if (obj->is_class("ScriptEditorDebuggerInspectedObject")) { - text = obj->call("get_title"); - } else { - text = obj->get_class(); - } - - if (i == editor_history.get_history_pos()) { - text = "[" + text + "]"; - } - editor_history_menu->get_popup()->add_icon_item(icon, text, i); - } -} - -void EditorNode::_property_editor_forward() { - - if (editor_history.next()) - _edit_current(); -} -void EditorNode::_property_editor_back() { - - if (editor_history.previous() || editor_history.get_path_size() == 1) - _edit_current(); -} - -void EditorNode::_menu_collapseall() { - - property_editor->collapse_all_folding(); -} - -void EditorNode::_menu_expandall() { - - property_editor->expand_all_folding(); -} - void EditorNode::_save_default_environment() { Ref<Environment> fallback = get_tree()->get_root()->get_world()->get_fallback_environment(); @@ -1493,52 +1364,38 @@ static bool overrides_external_editor(Object *p_object) { return script->get_language()->overrides_external_editor(); } -void EditorNode::_property_editable_warning_pressed() { - - property_editable_warning_dialog->popup_centered_minsize(); -} - void EditorNode::_edit_current() { uint32_t current = editor_history.get_current(); Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL; - property_back->set_disabled(editor_history.is_at_beginning()); - property_forward->set_disabled(editor_history.is_at_end()); - this->current = current_obj; - editor_path->update_path(); - - String editable_warning; //none by default - property_editable_warning->hide(); //hide by default if (!current_obj) { scene_tree_dock->set_selected(NULL); - property_editor->edit(NULL); + get_inspector()->edit(NULL); node_dock->set_node(NULL); - object_menu->set_disabled(true); + inspector_dock->update(NULL); _display_top_editors(false); return; } - object_menu->set_disabled(true); - bool capitalize = bool(EDITOR_DEF("interface/editor/capitalize_properties", true)); bool is_resource = current_obj->is_class("Resource"); bool is_node = current_obj->is_class("Node"); - resource_save_button->set_disabled(!is_resource); + + String editable_warning; //none by default if (is_resource) { Resource *current_res = Object::cast_to<Resource>(current_obj); ERR_FAIL_COND(!current_res); scene_tree_dock->set_selected(NULL); - property_editor->edit(current_res); + get_inspector()->edit(current_res); node_dock->set_node(NULL); - object_menu->set_disabled(false); EditorNode::get_singleton()->get_import_dock()->set_edit_path(current_res->get_path()); int subr_idx = current_res->get_path().find("::"); @@ -1561,7 +1418,7 @@ void EditorNode::_edit_current() { Node *current_node = Object::cast_to<Node>(current_obj); ERR_FAIL_COND(!current_node); - property_editor->edit(current_node); + get_inspector()->edit(current_node); if (current_node->is_inside_tree()) { node_dock->set_node(current_node); scene_tree_dock->set_selected(current_node); @@ -1569,7 +1426,6 @@ void EditorNode::_edit_current() { node_dock->set_node(NULL); scene_tree_dock->set_selected(NULL); } - object_menu->get_popup()->clear(); if (get_edited_scene() && get_edited_scene()->get_filename() != String()) { String source_scene = get_edited_scene()->get_filename(); @@ -1585,17 +1441,14 @@ void EditorNode::_edit_current() { capitalize = false; } - property_editor->edit(current_obj); + get_inspector()->edit(current_obj); node_dock->set_node(NULL); } - if (editable_warning != String()) { - property_editable_warning->show(); //hide by default - property_editable_warning_dialog->set_text(editable_warning); - } + inspector_dock->set_warning(editable_warning); - if (property_editor->is_capitalize_paths_enabled() != capitalize) { - property_editor->set_enable_capitalize_paths(capitalize); + if (get_inspector()->is_capitalize_paths_enabled() != capitalize) { + get_inspector()->set_enable_capitalize_paths(capitalize); } /* Take care of PLUGIN EDITOR */ @@ -1653,75 +1506,8 @@ void EditorNode::_edit_current() { _hide_top_editors(); } - object_menu->set_disabled(false); - - PopupMenu *p = object_menu->get_popup(); - - p->clear(); - p->add_shortcut(ED_SHORTCUT("property_editor/expand_all", TTR("Expand all properties")), EXPAND_ALL); - p->add_shortcut(ED_SHORTCUT("property_editor/collapse_all", TTR("Collapse all properties")), COLLAPSE_ALL); - p->add_separator(); - p->add_shortcut(ED_SHORTCUT("property_editor/copy_params", TTR("Copy Params")), OBJECT_COPY_PARAMS); - p->add_shortcut(ED_SHORTCUT("property_editor/paste_params", TTR("Paste Params")), OBJECT_PASTE_PARAMS); - p->add_separator(); - p->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTR("Paste Resource")), RESOURCE_PASTE); - if (is_resource) { - p->add_shortcut(ED_SHORTCUT("property_editor/copy_resource", TTR("Copy Resource")), RESOURCE_COPY); - p->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Built-In")), RESOURCE_UNREF); - } - - if (is_resource || is_node) { - p->add_separator(); - p->add_shortcut(ED_SHORTCUT("property_editor/make_subresources_unique", TTR("Make Sub-Resources Unique")), OBJECT_UNIQUE_RESOURCES); - p->add_separator(); - p->add_icon_shortcut(gui_base->get_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("property_editor/open_help", TTR("Open in Help")), OBJECT_REQUEST_HELP); - } - - List<MethodInfo> methods; - current_obj->get_method_list(&methods); - - if (!methods.empty()) { - - bool found = false; - List<MethodInfo>::Element *I = methods.front(); - int i = 0; - while (I) { - - if (I->get().flags & METHOD_FLAG_EDITOR) { - if (!found) { - p->add_separator(); - found = true; - } - p->add_item(I->get().name.capitalize(), OBJECT_METHOD_BASE + i); - } - i++; - I = I->next(); - } - } - - update_keying(); -} - -void EditorNode::_resource_created() { - - Object *c = create_dialog->instance_selected(); - - ERR_FAIL_COND(!c); - Resource *r = Object::cast_to<Resource>(c); - ERR_FAIL_COND(!r); - - REF res(r); - - push_item(c); -} - -void EditorNode::_resource_selected(const RES &p_res, const String &p_property) { - - if (p_res.is_null()) - return; - - RES r = p_res; - push_item(r.operator->(), p_property); + inspector_dock->update(current_obj); + inspector_dock->update_keying(); } void EditorNode::_run(bool p_current, const String &p_custom) { @@ -1795,7 +1581,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) { } } - if (bool(EDITOR_DEF("run/auto_save/save_before_running", true))) { + if (bool(EDITOR_GET("run/auto_save/save_before_running"))) { if (unsaved_cache) { @@ -1822,11 +1608,11 @@ void EditorNode::_run(bool p_current, const String &p_custom) { if (!call_build()) return; - if (bool(EDITOR_DEF("run/output/always_clear_output_on_play", true))) { + if (bool(EDITOR_GET("run/output/always_clear_output_on_play"))) { log->clear(); } - if (bool(EDITOR_DEF("run/output/always_open_output_on_play", true))) { + if (bool(EDITOR_GET("run/output/always_open_output_on_play"))) { make_bottom_panel_item_visible(log); } @@ -2162,136 +1948,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { scene_tabs->set_current_tab(cur_idx); } break; - case RESOURCE_NEW: { - - create_dialog->popup_create(true); - } break; - case RESOURCE_LOAD: { - - open_resource(); - } break; - case RESOURCE_SAVE: { - - uint32_t current = editor_history.get_current(); - Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL; - - ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj)) - - RES current_res = RES(Object::cast_to<Resource>(current_obj)); - - save_resource(current_res); - - } break; - case RESOURCE_SAVE_AS: { - - uint32_t current = editor_history.get_current(); - Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL; - - ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj)) - - RES current_res = RES(Object::cast_to<Resource>(current_obj)); - - save_resource_as(current_res); - - } break; - case RESOURCE_UNREF: { - - uint32_t current = editor_history.get_current(); - Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL; - - ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj)) - - RES current_res = RES(Object::cast_to<Resource>(current_obj)); - current_res->set_path(""); - _edit_current(); - } break; - case RESOURCE_COPY: { - - uint32_t current = editor_history.get_current(); - Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL; - - ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj)) - - RES current_res = RES(Object::cast_to<Resource>(current_obj)); - - EditorSettings::get_singleton()->set_resource_clipboard(current_res); - - } break; - case RESOURCE_PASTE: { - - RES r = EditorSettings::get_singleton()->get_resource_clipboard(); - if (r.is_valid()) { - push_item(EditorSettings::get_singleton()->get_resource_clipboard().ptr(), String()); - } - - } break; - case OBJECT_REQUEST_HELP: { - - if (current) { - _editor_select(EDITOR_SCRIPT); - emit_signal("request_help", current->get_class()); - } - - } break; - case OBJECT_COPY_PARAMS: { - - editor_data.apply_changes_in_editors(); - if (current) - editor_data.copy_object_params(current); - } break; - case OBJECT_PASTE_PARAMS: { - - editor_data.apply_changes_in_editors(); - if (current) - editor_data.paste_object_params(current); - editor_data.get_undo_redo().clear_history(); - } break; - case OBJECT_UNIQUE_RESOURCES: { - - editor_data.apply_changes_in_editors(); - if (current) { - List<PropertyInfo> props; - current->get_property_list(&props); - Map<RES, RES> duplicates; - for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) - continue; - - Variant v = current->get(E->get().name); - if (v.is_ref()) { - REF ref = v; - if (ref.is_valid()) { - - RES res = ref; - if (res.is_valid()) { - - if (!duplicates.has(res)) { - duplicates[res] = res->duplicate(); - } - res = duplicates[res]; - - current->set(E->get().name, res); - } - } - } - } - } - - editor_data.get_undo_redo().clear_history(); - - _set_editing_top_editors(NULL); - _set_editing_top_editors(current); - - } break; - case COLLAPSE_ALL: { - _menu_collapseall(); - - } break; - case EXPAND_ALL: { - _menu_expandall(); - - } break; case RUN_PLAY: { _menu_option_confirm(RUN_STOP, true); _run(false); @@ -2325,7 +1981,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { play_custom_scene_button->set_icon(gui_base->get_icon("PlayCustom", "EditorIcons")); stop_button->set_disabled(true); - if (bool(EDITOR_DEF("run/output/always_close_output_on_stop", true))) { + if (bool(EDITOR_GET("run/output/always_close_output_on_stop"))) { for (int i = 0; i < bottom_panel_items.size(); i++) { if (bottom_panel_items[i].control == log) { _bottom_panel_switch(false, i); @@ -2345,7 +2001,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; case RUN_PLAY_NATIVE: { - bool autosave = EDITOR_DEF("run/auto_save/save_before_running", true); + bool autosave = EDITOR_GET("run/auto_save/save_before_running"); if (autosave) { _menu_option_confirm(FILE_SAVE_ALL_SCENES, false); } @@ -2371,10 +2027,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { case RUN_PROJECT_MANAGER: { if (!p_confirmed) { - bool save_each = EDITOR_DEF("interface/editor/save_each_scene_on_quit", true); + bool save_each = EDITOR_GET("interface/editor/save_each_scene_on_quit"); if (_next_unsaved_scene(!save_each) == -1) { - bool confirm = EDITOR_DEF("interface/editor/quit_confirmation", true); + bool confirm = EDITOR_GET("interface/editor/quit_confirmation"); if (confirm) { confirmation->get_ok()->set_text(p_option == FILE_QUIT ? TTR("Quit") : TTR("Yes")); @@ -2554,22 +2210,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; default: { - - if (p_option >= OBJECT_METHOD_BASE) { - - ERR_FAIL_COND(!current); - - int idx = p_option - OBJECT_METHOD_BASE; - - List<MethodInfo> methods; - current->get_method_list(&methods); - - ERR_FAIL_INDEX(idx, methods.size()); - String name = methods[idx].name; - - if (current) - current->call(name); - } else if (p_option >= IMPORT_PLUGIN_BASE) { + if (p_option >= IMPORT_PLUGIN_BASE) { } } } @@ -2939,7 +2580,7 @@ Dictionary EditorNode::_get_main_scene_state() { Dictionary state; state["main_tab"] = _get_current_main_editor(); state["scene_tree_offset"] = scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value(); - state["property_edit_offset"] = get_property_editor()->get_scene_tree()->get_vscroll_bar()->get_value(); + state["property_edit_offset"] = get_inspector()->get_scroll_offset(); state["saved_version"] = saved_version; state["node_filter"] = scene_tree_dock->get_filter(); return state; @@ -2985,7 +2626,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { if (p_state.has("scene_tree_offset")) scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->set_value(p_state["scene_tree_offset"]); if (p_state.has("property_edit_offset")) - get_property_editor()->get_scene_tree()->get_vscroll_bar()->set_value(p_state["property_edit_offset"]); + get_inspector()->set_scroll_offset(p_state["property_edit_offset"]); if (p_state.has("node_filter")) scene_tree_dock->set_filter(p_state["node_filter"]); @@ -3243,44 +2884,14 @@ SceneTreeDock *EditorNode::get_scene_tree_dock() { return scene_tree_dock; } +InspectorDock *EditorNode::get_inspector_dock() { -void EditorNode::_instance_request(const Vector<String> &p_files) { - - request_instance_scenes(p_files); + return inspector_dock; } -void EditorNode::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) { - - AnimationPlayerEditor::singleton->get_key_editor()->insert_value_key(p_keyed, p_value, p_advance); -} - -void EditorNode::_transform_keyed(Object *sp, const String &p_sub, const Transform &p_key) { - - Spatial *s = Object::cast_to<Spatial>(sp); - if (!s) - return; - AnimationPlayerEditor::singleton->get_key_editor()->insert_transform_key(s, p_sub, p_key); -} - -void EditorNode::update_keying() { - - bool valid = false; - - if (AnimationPlayerEditor::singleton->get_key_editor()->has_keying()) { - - if (editor_history.get_path_size() >= 1) { - - Object *obj = ObjectDB::get_instance(editor_history.get_path_object(0)); - if (Object::cast_to<Node>(obj)) { - - valid = true; - } - } - } - - property_editor->set_keying(valid); +void EditorNode::_instance_request(const Vector<String> &p_files) { - AnimationPlayerEditor::singleton->get_key_editor()->update_keying(); + request_instance_scenes(p_files); } void EditorNode::_close_messages() { @@ -3425,6 +3036,9 @@ void EditorNode::register_editor_types() { ClassDB::register_class<EditorExportPlugin>(); ClassDB::register_class<EditorResourceConversionPlugin>(); ClassDB::register_class<EditorSceneImporter>(); + ClassDB::register_class<EditorInspector>(); + ClassDB::register_class<EditorInspectorPlugin>(); + ClassDB::register_class<EditorProperty>(); // FIXME: Is this stuff obsolete, or should it be ported to new APIs? ClassDB::register_class<EditorScenePostImport>(); @@ -4042,7 +3656,7 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String } void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout, const String &p_section) { - if (!bool(EDITOR_DEF("interface/scene_tabs/restore_scenes_on_load", false))) { + if (!bool(EDITOR_GET("interface/scene_tabs/restore_scenes_on_load"))) { return; } @@ -4136,7 +3750,7 @@ void EditorNode::_scene_tab_script_edited(int p_tab) { Ref<Script> script = editor_data.get_scene_root_script(p_tab); if (script.is_valid()) - edit_resource(script); + inspector_dock->edit_resource(script); } void EditorNode::_scene_tab_closed(int p_tab) { @@ -4164,7 +3778,7 @@ void EditorNode::_scene_tab_closed(int p_tab) { } void EditorNode::_scene_tab_hover(int p_tab) { - if (bool(EDITOR_DEF("interface/scene_tabs/show_thumbnail_on_hover", true)) == false) { + if (bool(EDITOR_GET("interface/scene_tabs/show_thumbnail_on_hover")) == false) { return; } int current_tab = scene_tabs->get_current_tab(); @@ -4234,30 +3848,6 @@ void EditorNode::_scene_tab_changed(int p_tab) { editor_data.get_undo_redo().commit_action(); } -void EditorNode::_toggle_search_bar(bool p_pressed) { - - property_editor->set_use_filter(p_pressed); - - if (p_pressed) { - - search_bar->show(); - search_box->grab_focus(); - search_box->select_all(); - } else { - - search_bar->hide(); - } -} - -void EditorNode::_clear_search_box() { - - if (search_box->get_text() == "") - return; - - search_box->clear(); - property_editor->update_tree(); -} - ToolButton *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) { ToolButton *tb = memnew(ToolButton); @@ -4681,12 +4271,21 @@ EditorBuildCallback EditorNode::build_callbacks[EditorNode::MAX_BUILD_CALLBACKS] bool EditorNode::call_build() { - for (int i = 0; i < build_callback_count; i++) { - if (!build_callbacks[i]()) - return false; + bool builds_successful = true; + + for (int i = 0; i < build_callback_count && builds_successful; i++) { + if (!build_callbacks[i]()) { + ERR_PRINT("A Godot Engine build callback failed."); + builds_successful = false; + } } - return true; + if (builds_successful && !editor_data.call_build()) { + ERR_PRINT("An EditorPlugin build callback failed."); + builds_successful = false; + } + + return builds_successful; } void EditorNode::_inherit_imported(const String &p_action) { @@ -4776,9 +4375,6 @@ void EditorNode::_bind_methods() { ClassDB::bind_method("_tool_menu_option", &EditorNode::_tool_menu_option); ClassDB::bind_method("_menu_confirm_current", &EditorNode::_menu_confirm_current); ClassDB::bind_method("_dialog_action", &EditorNode::_dialog_action); - ClassDB::bind_method("_resource_selected", &EditorNode::_resource_selected, DEFVAL("")); - ClassDB::bind_method("_property_editor_forward", &EditorNode::_property_editor_forward); - ClassDB::bind_method("_property_editor_back", &EditorNode::_property_editor_back); ClassDB::bind_method("_editor_select", &EditorNode::_editor_select); ClassDB::bind_method("_node_renamed", &EditorNode::_node_renamed); ClassDB::bind_method("edit_node", &EditorNode::edit_node); @@ -4788,16 +4384,12 @@ void EditorNode::_bind_methods() { ClassDB::bind_method("set_edited_scene", &EditorNode::set_edited_scene); ClassDB::bind_method("open_request", &EditorNode::open_request); ClassDB::bind_method("_instance_request", &EditorNode::_instance_request); - ClassDB::bind_method("update_keying", &EditorNode::update_keying); - ClassDB::bind_method("_property_keyed", &EditorNode::_property_keyed); - ClassDB::bind_method("_transform_keyed", &EditorNode::_transform_keyed); ClassDB::bind_method("_close_messages", &EditorNode::_close_messages); ClassDB::bind_method("_show_messages", &EditorNode::_show_messages); ClassDB::bind_method("_vp_resized", &EditorNode::_vp_resized); ClassDB::bind_method("_quick_opened", &EditorNode::_quick_opened); ClassDB::bind_method("_quick_run", &EditorNode::_quick_run); - ClassDB::bind_method("_resource_created", &EditorNode::_resource_created); ClassDB::bind_method("_open_recent_scene", &EditorNode::_open_recent_scene); ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process); @@ -4831,15 +4423,9 @@ void EditorNode::_bind_methods() { ClassDB::bind_method("_discard_changes", &EditorNode::_discard_changes); ClassDB::bind_method("_update_recent_scenes", &EditorNode::_update_recent_scenes); - ClassDB::bind_method("_prepare_history", &EditorNode::_prepare_history); - ClassDB::bind_method("_select_history", &EditorNode::_select_history); - - ClassDB::bind_method("_toggle_search_bar", &EditorNode::_toggle_search_bar); - ClassDB::bind_method("_clear_search_box", &EditorNode::_clear_search_box); ClassDB::bind_method("_clear_undo_history", &EditorNode::_clear_undo_history); ClassDB::bind_method("_dropped_files", &EditorNode::_dropped_files); ClassDB::bind_method("_toggle_distraction_free_mode", &EditorNode::_toggle_distraction_free_mode); - ClassDB::bind_method("_property_editable_warning_pressed", &EditorNode::_property_editable_warning_pressed); ClassDB::bind_method(D_METHOD("get_gui_base"), &EditorNode::get_gui_base); ClassDB::bind_method(D_METHOD("_bottom_panel_switch"), &EditorNode::_bottom_panel_switch); @@ -4853,7 +4439,6 @@ void EditorNode::_bind_methods() { ADD_SIGNAL(MethodInfo("play_pressed")); ADD_SIGNAL(MethodInfo("pause_pressed")); ADD_SIGNAL(MethodInfo("stop_pressed")); - ADD_SIGNAL(MethodInfo("request_help")); ADD_SIGNAL(MethodInfo("request_help_search")); ADD_SIGNAL(MethodInfo("request_help_index")); ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::POOL_STRING_ARRAY, "args"))); @@ -5007,6 +4592,12 @@ EditorNode::EditorNode() { ResourceFormatImporter::get_singleton()->add_importer(import_bitmap); } + { + Ref<EditorInspectorDefaultPlugin> eidp; + eidp.instance(); + EditorInspector::add_inspector_plugin(eidp); + } + _pvrtc_register_compressors(); editor_selection = memnew(EditorSelection); @@ -5034,6 +4625,22 @@ EditorNode::EditorNode() { ClassDB::set_class_enabled("CollisionShape2D", true); ClassDB::set_class_enabled("CollisionPolygon2D", true); + //defs here, use EDITOR_GET in logic + EDITOR_DEF("interface/scene_tabs/always_show_close_button", false); + EDITOR_DEF("interface/scene_tabs/resize_if_many_tabs", true); + EDITOR_DEF("interface/scene_tabs/minimum_width", 50); + EDITOR_DEF("run/output/always_clear_output_on_play", true); + EDITOR_DEF("run/output/always_open_output_on_play", true); + EDITOR_DEF("run/output/always_close_output_on_stop", true); + EDITOR_DEF("run/auto_save/save_before_running", true); + EDITOR_DEF("interface/editor/save_each_scene_on_quit", true); + EDITOR_DEF("interface/editor/quit_confirmation", true); + EDITOR_DEF("interface/scene_tabs/restore_scenes_on_load", false); + EDITOR_DEF("interface/scene_tabs/show_thumbnail_on_hover", true); + EDITOR_DEF("interface/inspector/capitalize_properties", true); + EDITOR_DEF("interface/inspector/open_resources_in_new_inspector", false); + EDITOR_DEF("run/auto_save/save_before_running", true); + theme_base = memnew(Control); add_child(theme_base); theme_base->set_anchors_and_margins_preset(Control::PRESET_WIDE); @@ -5557,129 +5164,11 @@ EditorNode::EditorNode() { dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(scene_tree_dock->get_index(), TTR("Scene")); dock_slot[DOCK_SLOT_LEFT_BR]->hide(); - VBoxContainer *prop_editor_base = memnew(VBoxContainer); - prop_editor_base->set_name("Inspector"); - dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(prop_editor_base); - dock_slot[DOCK_SLOT_RIGHT_BL]->set_tab_title(prop_editor_base->get_index(), TTR("Inspector")); - - HBoxContainer *prop_editor_hb = memnew(HBoxContainer); - - prop_editor_base->add_child(prop_editor_hb); - prop_editor_vb = prop_editor_base; - - resource_new_button = memnew(ToolButton); - resource_new_button->set_tooltip(TTR("Create a new resource in memory and edit it.")); - resource_new_button->set_icon(gui_base->get_icon("New", "EditorIcons")); - prop_editor_hb->add_child(resource_new_button); - resource_new_button->connect("pressed", this, "_menu_option", varray(RESOURCE_NEW)); - resource_new_button->set_focus_mode(Control::FOCUS_NONE); - - resource_load_button = memnew(ToolButton); - resource_load_button->set_tooltip(TTR("Load an existing resource from disk and edit it.")); - resource_load_button->set_icon(gui_base->get_icon("Load", "EditorIcons")); - prop_editor_hb->add_child(resource_load_button); - resource_load_button->connect("pressed", this, "_menu_option", varray(RESOURCE_LOAD)); - resource_load_button->set_focus_mode(Control::FOCUS_NONE); - - resource_save_button = memnew(MenuButton); - resource_save_button->set_tooltip(TTR("Save the currently edited resource.")); - resource_save_button->set_icon(gui_base->get_icon("Save", "EditorIcons")); - prop_editor_hb->add_child(resource_save_button); - resource_save_button->get_popup()->add_item(TTR("Save"), RESOURCE_SAVE); - resource_save_button->get_popup()->add_item(TTR("Save As..."), RESOURCE_SAVE_AS); - resource_save_button->get_popup()->connect("id_pressed", this, "_menu_option"); - resource_save_button->set_focus_mode(Control::FOCUS_NONE); - resource_save_button->set_disabled(true); - - prop_editor_hb->add_spacer(); - - property_back = memnew(ToolButton); - property_back->set_icon(gui_base->get_icon("Back", "EditorIcons")); - property_back->set_flat(true); - property_back->set_tooltip(TTR("Go to the previous edited object in history.")); - property_back->set_disabled(true); - - prop_editor_hb->add_child(property_back); - - property_forward = memnew(ToolButton); - property_forward->set_icon(gui_base->get_icon("Forward", "EditorIcons")); - property_forward->set_flat(true); - property_forward->set_tooltip(TTR("Go to the next edited object in history.")); - property_forward->set_disabled(true); - - prop_editor_hb->add_child(property_forward); - - editor_history_menu = memnew(MenuButton); - editor_history_menu->set_tooltip(TTR("History of recently edited objects.")); - editor_history_menu->set_icon(gui_base->get_icon("History", "EditorIcons")); - prop_editor_hb->add_child(editor_history_menu); - editor_history_menu->connect("about_to_show", this, "_prepare_history"); - editor_history_menu->get_popup()->connect("id_pressed", this, "_select_history"); - - prop_editor_hb = memnew(HBoxContainer); //again... - prop_editor_base->add_child(prop_editor_hb); - - editor_path = memnew(EditorPath(&editor_history)); - editor_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); - prop_editor_hb->add_child(editor_path); - - search_button = memnew(ToolButton); - search_button->set_toggle_mode(true); - search_button->set_pressed(false); - search_button->set_icon(gui_base->get_icon("Search", "EditorIcons")); - prop_editor_hb->add_child(search_button); - search_button->connect("toggled", this, "_toggle_search_bar"); - - object_menu = memnew(MenuButton); - object_menu->set_icon(gui_base->get_icon("Tools", "EditorIcons")); - prop_editor_hb->add_child(object_menu); - object_menu->set_tooltip(TTR("Object properties.")); - - create_dialog = memnew(CreateDialog); - gui_base->add_child(create_dialog); - create_dialog->set_base_type("Resource"); - create_dialog->connect("create", this, "_resource_created"); - - search_bar = memnew(HBoxContainer); - search_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL); - prop_editor_base->add_child(search_bar); - search_bar->hide(); - - Label *l = memnew(Label(TTR("Search:") + " ")); - search_bar->add_child(l); - - search_box = memnew(LineEdit); - search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); - search_bar->add_child(search_box); - - ToolButton *clear_button = memnew(ToolButton); - clear_button->set_icon(gui_base->get_icon("Close", "EditorIcons")); - search_bar->add_child(clear_button); - clear_button->connect("pressed", this, "_clear_search_box"); - - property_editable_warning = memnew(Button); - property_editable_warning->set_text(TTR("Changes may be lost!")); - prop_editor_base->add_child(property_editable_warning); - property_editable_warning_dialog = memnew(AcceptDialog); - gui_base->add_child(property_editable_warning_dialog); - property_editable_warning->hide(); - property_editable_warning->connect("pressed", this, "_property_editable_warning_pressed"); - - property_editor = memnew(PropertyEditor); - property_editor->set_autoclear(true); - property_editor->set_show_categories(true); - property_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); - property_editor->set_use_doc_hints(true); - property_editor->set_hide_script(false); - property_editor->set_enable_capitalize_paths(bool(EDITOR_DEF("interface/editor/capitalize_properties", true))); - property_editor->set_use_folding(!bool(EDITOR_DEF("interface/editor/disable_inspector_folding", false))); - - property_editor->hide_top_label(); - property_editor->register_text_enter(search_box); + inspector_dock = memnew(InspectorDock(this, editor_data)); + dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(inspector_dock); + dock_slot[DOCK_SLOT_RIGHT_BL]->set_tab_title(inspector_dock->get_index(), TTR("Inspector")); Button *property_editable_warning; - prop_editor_base->add_child(property_editor); - property_editor->set_undo_redo(&editor_data.get_undo_redo()); import_dock = memnew(ImportDock); dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(import_dock); @@ -5805,18 +5294,12 @@ EditorNode::EditorNode() { gui_base->add_child(file_script); file_script->connect("file_selected", this, "_dialog_action"); - property_forward->connect("pressed", this, "_property_editor_forward"); - property_back->connect("pressed", this, "_property_editor_back"); - file_menu->get_popup()->connect("id_pressed", this, "_menu_option"); - object_menu->get_popup()->connect("id_pressed", this, "_menu_option"); settings_menu->get_popup()->connect("id_pressed", this, "_menu_option"); file->connect("file_selected", this, "_dialog_action"); file_templates->connect("file_selected", this, "_dialog_action"); - property_editor->connect("resource_selected", this, "_resource_selected"); - property_editor->connect("property_keyed", this, "_property_keyed"); //plugin stuff @@ -5905,6 +5388,7 @@ EditorNode::EditorNode() { //resource_preview->add_preview_generator( Ref<EditorSamplePreviewPlugin>( memnew(EditorSamplePreviewPlugin ))); resource_preview->add_preview_generator(Ref<EditorMeshPreviewPlugin>(memnew(EditorMeshPreviewPlugin))); resource_preview->add_preview_generator(Ref<EditorBitmapPreviewPlugin>(memnew(EditorBitmapPreviewPlugin))); + resource_preview->add_preview_generator(Ref<EditorFontPreviewPlugin>(memnew(EditorFontPreviewPlugin))); { Ref<SpatialMaterialConversionPlugin> spatial_mat_convert; @@ -6039,6 +5523,8 @@ EditorNode::EditorNode() { EditorNode::~EditorNode() { + EditorInspector::cleanup_plugins(); + remove_print_handler(&print_handler); memdelete(EditorHelp::get_doc_data()); memdelete(editor_selection); diff --git a/editor/editor_node.h b/editor/editor_node.h index f774fa0a2e..bef5bc816c 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -37,9 +37,9 @@ #include "editor/editor_about.h" #include "editor/editor_data.h" #include "editor/editor_export.h" +#include "editor/editor_inspector.h" #include "editor/editor_log.h" #include "editor/editor_name_dialog.h" -#include "editor/editor_path.h" #include "editor/editor_plugin.h" #include "editor/editor_resource_preview.h" #include "editor/editor_run.h" @@ -52,6 +52,7 @@ #include "editor/filesystem_dock.h" #include "editor/groups_editor.h" #include "editor/import_dock.h" +#include "editor/inspector_dock.h" #include "editor/node_dock.h" #include "editor/pane_drag.h" #include "editor/progress_dialog.h" @@ -80,7 +81,6 @@ #include "scene/gui/tool_button.h" #include "scene/gui/tree.h" #include "scene/gui/viewport_container.h" - /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -142,22 +142,10 @@ private: EDIT_REVERT, TOOLS_ORPHAN_RESOURCES, TOOLS_CUSTOM, - RESOURCE_NEW, - RESOURCE_LOAD, RESOURCE_SAVE, RESOURCE_SAVE_AS, - RESOURCE_UNREF, - RESOURCE_COPY, - RESOURCE_PASTE, - OBJECT_COPY_PARAMS, - OBJECT_PASTE_PARAMS, - OBJECT_UNIQUE_RESOURCES, - OBJECT_REQUEST_HELP, RUN_PLAY, - COLLAPSE_ALL, - EXPAND_ALL, - RUN_STOP, RUN_PLAY_SCENE, RUN_PLAY_NATIVE, @@ -195,8 +183,6 @@ private: IMPORT_PLUGIN_BASE = 100, - OBJECT_METHOD_BASE = 500, - TOOL_MENU_BASE = 1000 }; @@ -247,7 +233,6 @@ private: PopupMenu *tool_menu; ToolButton *export_button; ToolButton *prev_scene; - MenuButton *object_menu; ToolButton *play_button; MenuButton *native_play_button; ToolButton *pause_button; @@ -264,24 +249,13 @@ private: Ref<Theme> theme; PopupMenu *recent_scenes; - Button *property_back; - Button *property_forward; SceneTreeDock *scene_tree_dock; - PropertyEditor *property_editor; - Button *property_editable_warning; - AcceptDialog *property_editable_warning_dialog; - void _property_editable_warning_pressed(); + InspectorDock *inspector_dock; NodeDock *node_dock; ImportDock *import_dock; - VBoxContainer *prop_editor_vb; FileSystemDock *filesystem_dock; EditorRunNative *run_native; - HBoxContainer *search_bar; - LineEdit *search_box; - - CreateDialog *create_dialog; - ConfirmationDialog *confirmation; ConfirmationDialog *save_confirmation; ConfirmationDialog *import_confirmation; @@ -314,11 +288,6 @@ private: String defer_export_platform; bool defer_export_debug; Node *_last_instanced_scene; - EditorPath *editor_path; - ToolButton *resource_new_button; - ToolButton *resource_load_button; - MenuButton *resource_save_button; - MenuButton *editor_history_menu; EditorLog *log; CenterContainer *tabs_center; @@ -422,23 +391,12 @@ private: void _dialog_display_load_error(String p_file, Error p_error); int current_option; - void _resource_created(); - void _resource_selected(const RES &p_res, const String &p_property = ""); void _menu_option(int p_option); void _menu_confirm_current(); void _menu_option_confirm(int p_option, bool p_confirmed); void _tool_menu_option(int p_idx); void _update_debug_options(); - void _property_editor_forward(); - void _property_editor_back(); - - void _menu_collapseall(); - void _menu_expandall(); - - void _select_history(int p_idx); - void _prepare_history(); - void _fs_changed(); void _resources_reimported(const Vector<String> &p_resources); void _sources_changed(bool p_exist); @@ -462,9 +420,6 @@ private: void _instance_request(const Vector<String> &p_files); - void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance); - void _transform_keyed(Object *sp, const String &p_sub, const Transform &p_key); - void _hide_top_editors(); void _display_top_editors(bool p_display); void _set_top_editors(Vector<EditorPlugin *> p_editor_plugins_over); @@ -578,8 +533,6 @@ private: void _update_layouts_menu(); void _layout_menu_option(int p_id); - void _toggle_search_bar(bool p_pressed); - void _clear_search_box(); void _clear_undo_history(); void _update_addon_config(); @@ -640,8 +593,8 @@ public: EditorPluginList *get_editor_plugins_over() { return editor_plugins_over; } EditorPluginList *get_editor_plugins_force_over() { return editor_plugins_force_over; } EditorPluginList *get_editor_plugins_force_input_forwarding() { return editor_plugins_force_input_forwarding; } - PropertyEditor *get_property_editor() { return property_editor; } - VBoxContainer *get_property_editor_vb() { return prop_editor_vb; } + EditorInspector *get_inspector() { return inspector_dock->get_inspector(); } + Container *get_inspector_dock_addon_area() { return inspector_dock->get_addon_area(); } ProjectSettingsEditor *get_project_settings() { return project_settings; } @@ -663,8 +616,8 @@ public: bool is_addon_plugin_enabled(const String &p_addon) const; void edit_node(Node *p_node); - void edit_resource(const Ref<Resource> &p_resource); - void open_resource(const String &p_type = ""); + void edit_resource(const Ref<Resource> &p_resource) { inspector_dock->edit_resource(p_resource); }; + void open_resource(const String &p_type) { inspector_dock->open_resource(p_type); }; void save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path); void save_resource(const Ref<Resource> &p_resource); @@ -713,6 +666,7 @@ public: FileSystemDock *get_filesystem_dock(); ImportDock *get_import_dock(); SceneTreeDock *get_scene_tree_dock(); + InspectorDock *get_inspector_dock(); static UndoRedo *get_undo_redo() { return &singleton->editor_data.get_undo_redo(); } EditorSelection *get_editor_selection() { return editor_selection; } @@ -759,8 +713,6 @@ public: void save_layout(); - void update_keying(); - void open_export_template_manager(); void reload_scene(const String &p_path); @@ -785,6 +737,10 @@ public: void dim_editor(bool p_dimming); + void edit_current() { _edit_current(); }; + + void update_keying() const { inspector_dock->update_keying(); }; + EditorNode(); ~EditorNode(); void get_singleton(const char *arg1, bool arg2); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 2e4e887165..cc44938c25 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -375,7 +375,7 @@ void EditorPlugin::add_control_to_container(CustomControlContainer p_location, C } break; case CONTAINER_PROPERTY_EDITOR_BOTTOM: { - EditorNode::get_singleton()->get_property_editor_vb()->add_child(p_control); + EditorNode::get_singleton()->get_inspector_dock_addon_area()->add_child(p_control); } break; } @@ -422,7 +422,7 @@ void EditorPlugin::remove_control_from_container(CustomControlContainer p_locati } break; case CONTAINER_PROPERTY_EDITOR_BOTTOM: { - EditorNode::get_singleton()->get_property_editor_vb()->remove_child(p_control); + EditorNode::get_singleton()->get_inspector_dock_addon_area()->remove_child(p_control); } break; } @@ -659,6 +659,14 @@ void EditorPlugin::remove_export_plugin(const Ref<EditorExportPlugin> &p_exporte EditorExport::get_singleton()->remove_export_plugin(p_exporter); } +void EditorPlugin::add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) { + EditorInspector::add_inspector_plugin(p_plugin); +} + +void EditorPlugin::remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) { + EditorInspector::remove_inspector_plugin(p_plugin); +} + void EditorPlugin::add_scene_import_plugin(const Ref<EditorSceneImporter> &p_importer) { ResourceImporterScene::get_singleton()->add_importer(p_importer); } @@ -681,6 +689,15 @@ void EditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) { } } +bool EditorPlugin::build() { + + if (get_script_instance() && get_script_instance()->has_method("build")) { + return get_script_instance()->call("build"); + } + + return true; +} + void EditorPlugin::queue_save_layout() const { EditorNode::get_singleton()->save_layout(); @@ -728,8 +745,10 @@ void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_import_plugin", "importer"), &EditorPlugin::remove_import_plugin); ClassDB::bind_method(D_METHOD("add_scene_import_plugin", "scene_importer"), &EditorPlugin::add_scene_import_plugin); ClassDB::bind_method(D_METHOD("remove_scene_import_plugin", "scene_importer"), &EditorPlugin::remove_scene_import_plugin); - ClassDB::bind_method(D_METHOD("add_export_plugin", "exporter"), &EditorPlugin::add_export_plugin); - ClassDB::bind_method(D_METHOD("remove_export_plugin", "exporter"), &EditorPlugin::remove_export_plugin); + ClassDB::bind_method(D_METHOD("add_export_plugin", "plugin"), &EditorPlugin::add_export_plugin); + ClassDB::bind_method(D_METHOD("remove_export_plugin", "plugin"), &EditorPlugin::remove_export_plugin); + ClassDB::bind_method(D_METHOD("add_inspector_plugin", "plugin"), &EditorPlugin::add_inspector_plugin); + ClassDB::bind_method(D_METHOD("remove_inspector_plugin", "plugin"), &EditorPlugin::remove_inspector_plugin); ClassDB::bind_method(D_METHOD("set_input_event_forwarding_always_enabled"), &EditorPlugin::set_input_event_forwarding_always_enabled); ClassDB::bind_method(D_METHOD("set_force_draw_over_forwarding_enabled"), &EditorPlugin::set_force_draw_over_forwarding_enabled); @@ -757,6 +776,7 @@ void EditorPlugin::_bind_methods() { ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::POOL_STRING_ARRAY, "get_breakpoints")); ClassDB::add_virtual_method(get_class_static(), MethodInfo("set_window_layout", PropertyInfo(Variant::OBJECT, "layout", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo("get_window_layout", PropertyInfo(Variant::OBJECT, "layout", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "build")); ADD_SIGNAL(MethodInfo("scene_changed", PropertyInfo(Variant::OBJECT, "scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("scene_closed", PropertyInfo(Variant::STRING, "filepath"))); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index ebc4afdfeb..fcc74cb1e9 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -31,6 +31,7 @@ #ifndef EDITOR_PLUGIN_H #define EDITOR_PLUGIN_H +#include "editor/editor_inspector.h" #include "editor/import/editor_import_plugin.h" #include "editor/import/resource_importer_scene.h" #include "io/config_file.h" @@ -191,6 +192,7 @@ public: virtual void set_window_layout(Ref<ConfigFile> p_layout); virtual void get_window_layout(Ref<ConfigFile> p_layout); virtual void edited_scene_changed() {} // if changes are pending in editor, apply them + virtual bool build(); // builds with external tools. Returns true if safe to continue running scene. EditorInterface *get_editor_interface(); @@ -210,6 +212,9 @@ public: void add_export_plugin(const Ref<EditorExportPlugin> &p_exporter); void remove_export_plugin(const Ref<EditorExportPlugin> &p_exporter); + void add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin); + void remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin); + void add_scene_import_plugin(const Ref<EditorSceneImporter> &p_importer); void remove_scene_import_plugin(const Ref<EditorSceneImporter> &p_importer); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp new file mode 100644 index 0000000000..c6d3a43f4e --- /dev/null +++ b/editor/editor_properties.cpp @@ -0,0 +1,2695 @@ +/*************************************************************************/ +/* editor_properties.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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_properties.h" +#include "editor/editor_resource_preview.h" +#include "editor_node.h" +#include "editor_properties_array_dict.h" +#include "scene/main/viewport.h" + +///////////////////// NULL ///////////////////////// + +void EditorPropertyNil::update_property() { +} + +EditorPropertyNil::EditorPropertyNil() { + Label *label = memnew(Label); + label->set_text("[null]"); + add_child(label); +} + +///////////////////// TEXT ///////////////////////// +void EditorPropertyText::_text_changed(const String &p_string) { + if (updating) + return; + + emit_signal("property_changed", get_edited_property(), p_string); +} + +void EditorPropertyText::update_property() { + String s = get_edited_object()->get(get_edited_property()); + updating = true; + text->set_text(s); + text->set_editable(!is_read_only()); + updating = false; +} + +void EditorPropertyText::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_text_changed", "txt"), &EditorPropertyText::_text_changed); +} + +EditorPropertyText::EditorPropertyText() { + text = memnew(LineEdit); + add_child(text); + add_focusable(text); + text->connect("text_changed", this, "_text_changed"); + updating = false; +} + +///////////////////// MULTILINE TEXT ///////////////////////// + +void EditorPropertyMultilineText::_big_text_changed() { + text->set_text(big_text->get_text()); + emit_signal("property_changed", get_edited_property(), big_text->get_text()); +} + +void EditorPropertyMultilineText::_text_changed() { + + emit_signal("property_changed", get_edited_property(), text->get_text()); +} + +void EditorPropertyMultilineText::_open_big_text() { + + if (!big_text_dialog) { + big_text = memnew(TextEdit); + big_text->connect("text_changed", this, "_big_text_changed"); + big_text_dialog = memnew(AcceptDialog); + big_text_dialog->add_child(big_text); + big_text_dialog->set_title("Edit Text:"); + add_child(big_text_dialog); + } + + big_text->set_text(text->get_text()); + big_text_dialog->popup_centered_ratio(); +} + +void EditorPropertyMultilineText::update_property() { + String t = get_edited_object()->get(get_edited_property()); + text->set_text(t); + if (big_text && big_text->is_visible_in_tree()) { + big_text->set_text(t); + } +} + +void EditorPropertyMultilineText::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_ENTER_TREE: { + Ref<Texture> df = get_icon("DistractionFree", "EditorIcons"); + open_big_text->set_icon(df); + Ref<Font> font = get_font("font", "Label"); + text->set_custom_minimum_size(Vector2(0, font->get_height() * 6)); + + } break; + } +} + +void EditorPropertyMultilineText::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_text_changed"), &EditorPropertyMultilineText::_text_changed); + ClassDB::bind_method(D_METHOD("_big_text_changed"), &EditorPropertyMultilineText::_big_text_changed); + ClassDB::bind_method(D_METHOD("_open_big_text"), &EditorPropertyMultilineText::_open_big_text); +} + +EditorPropertyMultilineText::EditorPropertyMultilineText() { + HBoxContainer *hb = memnew(HBoxContainer); + add_child(hb); + set_bottom_editor(hb); + text = memnew(TextEdit); + text->connect("text_changed", this, "_text_changed"); + add_focusable(text); + hb->add_child(text); + text->set_h_size_flags(SIZE_EXPAND_FILL); + open_big_text = memnew(ToolButton); + open_big_text->connect("pressed", this, "_open_big_text"); + hb->add_child(open_big_text); + big_text_dialog = NULL; + big_text = NULL; +} + +///////////////////// TEXT ENUM ///////////////////////// + +void EditorPropertyTextEnum::_option_selected(int p_which) { + + emit_signal("property_changed", get_edited_property(), options->get_item_text(p_which)); +} + +void EditorPropertyTextEnum::update_property() { + + String which = get_edited_object()->get(get_edited_property()); + for (int i = 0; i < options->get_item_count(); i++) { + String t = options->get_item_text(i); + if (t == which) { + options->select(i); + return; + } + } +} + +void EditorPropertyTextEnum::setup(const Vector<String> &p_options) { + for (int i = 0; i < p_options.size(); i++) { + options->add_item(p_options[i], i); + } +} + +void EditorPropertyTextEnum::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_option_selected"), &EditorPropertyTextEnum::_option_selected); +} + +EditorPropertyTextEnum::EditorPropertyTextEnum() { + options = memnew(OptionButton); + options->set_clip_text(true); + add_child(options); + add_focusable(options); + options->connect("item_selected", this, "_option_selected"); +} +///////////////////// PATH ///////////////////////// + +void EditorPropertyPath::_path_selected(const String &p_path) { + + emit_signal("property_changed", get_edited_property(), p_path); + update_property(); +} +void EditorPropertyPath::_path_pressed() { + + if (!dialog) { + dialog = memnew(EditorFileDialog); + dialog->connect("file_selected", this, "_path_selected"); + dialog->connect("dir_selected", this, "_path_selected"); + add_child(dialog); + } + + String full_path = get_edited_object()->get(get_edited_property()); + + dialog->clear_filters(); + + if (global) { + dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + } else { + dialog->set_access(EditorFileDialog::ACCESS_RESOURCES); + } + + if (folder) { + dialog->set_mode(EditorFileDialog::MODE_OPEN_DIR); + dialog->set_current_dir(full_path); + } else { + dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); + for (int i = 0; i < extensions.size(); i++) { + String e = extensions[i].strip_edges(); + if (e != String()) { + dialog->add_filter(extensions[i].strip_edges()); + } + } + dialog->set_current_path(full_path); + } + + dialog->popup_centered_ratio(); +} + +void EditorPropertyPath::update_property() { + + String full_path = get_edited_object()->get(get_edited_property()); + path->set_text(full_path); + path->set_tooltip(full_path); +} + +void EditorPropertyPath::setup(const Vector<String> &p_extensions, bool p_folder, bool p_global) { + + extensions = p_extensions; + folder = p_folder; + global = p_global; +} + +void EditorPropertyPath::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_path_pressed"), &EditorPropertyPath::_path_pressed); + ClassDB::bind_method(D_METHOD("_path_selected"), &EditorPropertyPath::_path_selected); +} + +EditorPropertyPath::EditorPropertyPath() { + path = memnew(Button); + path->set_clip_text(true); + add_child(path); + add_focusable(path); + dialog = NULL; + path->connect("pressed", this, "_path_pressed"); + folder = false; + global = false; +} + +///////////////////// MEMBER ///////////////////////// + +void EditorPropertyMember::_property_selected(const String &p_selected) { + + emit_signal("property_changed", get_edited_property(), p_selected); + update_property(); +} + +void EditorPropertyMember::_property_select() { + + if (!selector) { + selector = memnew(PropertySelector); + selector->connect("selected", this, "_property_selected"); + add_child(selector); + } + + String current = get_edited_object()->get(get_edited_property()); + + if (hint == MEMBER_METHOD_OF_VARIANT_TYPE) { + + Variant::Type type = Variant::NIL; + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (hint_text == Variant::get_type_name(Variant::Type(i))) { + type = Variant::Type(i); + } + } + if (type) + selector->select_method_from_basic_type(type, current); + + } else if (hint == MEMBER_METHOD_OF_BASE_TYPE) { + + selector->select_method_from_base_type(hint_text, current); + + } else if (hint == MEMBER_METHOD_OF_INSTANCE) { + + Object *instance = ObjectDB::get_instance(hint_text.to_int64()); + if (instance) + selector->select_method_from_instance(instance, current); + + } else if (hint == MEMBER_METHOD_OF_SCRIPT) { + + Object *obj = ObjectDB::get_instance(hint_text.to_int64()); + if (Object::cast_to<Script>(obj)) { + selector->select_method_from_script(Object::cast_to<Script>(obj), current); + } + + } else if (hint == MEMBER_PROPERTY_OF_VARIANT_TYPE) { + + Variant::Type type = Variant::NIL; + String tname = hint_text; + if (tname.find(".") != -1) + tname = tname.get_slice(".", 0); + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (tname == Variant::get_type_name(Variant::Type(i))) { + type = Variant::Type(Variant::Type(i)); + } + } + + if (type != Variant::NIL) + selector->select_property_from_basic_type(type, current); + + } else if (hint == MEMBER_PROPERTY_OF_BASE_TYPE) { + + selector->select_property_from_base_type(hint_text, current); + + } else if (hint == MEMBER_PROPERTY_OF_INSTANCE) { + + Object *instance = ObjectDB::get_instance(hint_text.to_int64()); + if (instance) + selector->select_property_from_instance(instance, current); + + } else if (hint == MEMBER_PROPERTY_OF_SCRIPT) { + + Object *obj = ObjectDB::get_instance(hint_text.to_int64()); + if (Object::cast_to<Script>(obj)) { + selector->select_property_from_script(Object::cast_to<Script>(obj), current); + } + } +} + +void EditorPropertyMember::setup(Type p_hint, const String &p_hint_text) { + hint = p_hint; + hint_text = p_hint_text; +} + +void EditorPropertyMember::update_property() { + + String full_path = get_edited_object()->get(get_edited_property()); + property->set_text(full_path); +} + +void EditorPropertyMember::_bind_methods() { + ClassDB::bind_method(D_METHOD("_property_selected"), &EditorPropertyMember::_property_selected); + ClassDB::bind_method(D_METHOD("_property_select"), &EditorPropertyMember::_property_select); +} + +EditorPropertyMember::EditorPropertyMember() { + selector = NULL; + property = memnew(Button); + property->set_clip_text(true); + add_child(property); + add_focusable(property); + property->connect("pressed", this, "_property_select"); +} + +///////////////////// CHECK ///////////////////////// +void EditorPropertyCheck::_checkbox_pressed() { + + emit_signal("property_changed", get_edited_property(), checkbox->is_pressed()); +} + +void EditorPropertyCheck::update_property() { + bool c = get_edited_object()->get(get_edited_property()); + checkbox->set_pressed(c); + checkbox->set_disabled(is_read_only()); +} + +void EditorPropertyCheck::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_checkbox_pressed"), &EditorPropertyCheck::_checkbox_pressed); +} + +EditorPropertyCheck::EditorPropertyCheck() { + checkbox = memnew(CheckBox); + checkbox->set_text(TTR("On")); + add_child(checkbox); + add_focusable(checkbox); + checkbox->connect("pressed", this, "_checkbox_pressed"); +} + +///////////////////// ENUM ///////////////////////// + +void EditorPropertyEnum::_option_selected(int p_which) { + + emit_signal("property_changed", get_edited_property(), p_which); +} + +void EditorPropertyEnum::update_property() { + + int which = get_edited_object()->get(get_edited_property()); + options->select(which); +} + +void EditorPropertyEnum::setup(const Vector<String> &p_options) { + for (int i = 0; i < p_options.size(); i++) { + options->add_item(p_options[i], i); + } +} + +void EditorPropertyEnum::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_option_selected"), &EditorPropertyEnum::_option_selected); +} + +EditorPropertyEnum::EditorPropertyEnum() { + options = memnew(OptionButton); + options->set_clip_text(true); + add_child(options); + add_focusable(options); + options->connect("item_selected", this, "_option_selected"); +} + +///////////////////// FLAGS ///////////////////////// + +void EditorPropertyFlags::_flag_toggled() { + + uint32_t value = 0; + for (int i = 0; i < flags.size(); i++) { + if (flags[i]->is_pressed()) { + uint32_t val = 1; + val <<= flag_indices[i]; + value |= val; + } + } + + emit_signal("property_changed", get_edited_property(), value); +} + +void EditorPropertyFlags::update_property() { + + uint32_t value = get_edited_object()->get(get_edited_property()); + + for (int i = 0; i < flags.size(); i++) { + uint32_t val = 1; + val <<= flag_indices[i]; + if (value & val) { + + flags[i]->set_pressed(true); + } else { + flags[i]->set_pressed(false); + } + } +} + +void EditorPropertyFlags::setup(const Vector<String> &p_options) { + ERR_FAIL_COND(flags.size()); + + bool first = true; + for (int i = 0; i < p_options.size(); i++) { + String option = p_options[i].strip_edges(); + if (option != "") { + CheckBox *cb = memnew(CheckBox); + cb->set_text(option); + cb->set_clip_text(true); + cb->connect("pressed", this, "_flag_toggled"); + add_focusable(cb); + vbox->add_child(cb); + flags.push_back(cb); + flag_indices.push_back(i); + if (first) { + set_label_reference(cb); + first = false; + } + } + } +} + +void EditorPropertyFlags::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_flag_toggled"), &EditorPropertyFlags::_flag_toggled); +} + +EditorPropertyFlags::EditorPropertyFlags() { + + vbox = memnew(VBoxContainer); + add_child(vbox); +} + +///////////////////// LAYERS ///////////////////////// + +class EditorPropertyLayersGrid : public Control { + GDCLASS(EditorPropertyLayersGrid, Control) +public: + uint32_t value; + Vector<Rect2> flag_rects; + Vector<String> names; + + virtual Size2 get_minimum_size() const { + Ref<Font> font = get_font("font", "Label"); + return Vector2(0, font->get_height() * 2); + } + + virtual String get_tooltip(const Point2 &p_pos) const { + for (int i = 0; i < flag_rects.size(); i++) { + if (i < names.size() && flag_rects[i].has_point(p_pos)) { + return names[i]; + } + } + return String(); + } + void _gui_input(const Ref<InputEvent> &p_ev) { + Ref<InputEventMouseButton> mb = p_ev; + if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) { + for (int i = 0; i < flag_rects.size(); i++) { + if (flag_rects[i].has_point(mb->get_position())) { + //toggle + if (value & (1 << i)) { + value &= ~(1 << i); + } else { + value |= (1 << i); + } + emit_signal("flag_changed", value); + update(); + } + } + } + } + + void _notification(int p_what) { + if (p_what == NOTIFICATION_DRAW) { + + Rect2 rect; + rect.size = get_size(); + flag_rects.clear(); + + int bsize = (rect.size.height * 80 / 100) / 2; + + int h = bsize * 2 + 1; + int vofs = (rect.size.height - h) / 2; + + for (int i = 0; i < 2; i++) { + + Point2 ofs(4, vofs); + if (i == 1) + ofs.y += bsize + 1; + + ofs += rect.position; + for (int j = 0; j < 10; j++) { + + Point2 o = ofs + Point2(j * (bsize + 1), 0); + if (j >= 5) + o.x += 1; + + uint32_t idx = i * 10 + j; + bool on = value & (1 << idx); + Rect2 rect = Rect2(o, Size2(bsize, bsize)); + draw_rect(rect, Color(0, 0, 0, on ? 0.8 : 0.3)); + flag_rects.push_back(rect); + } + } + } + } + + void set_flag(uint32_t p_flag) { + value = p_flag; + update(); + } + + static void _bind_methods() { + + ClassDB::bind_method(D_METHOD("_gui_input"), &EditorPropertyLayersGrid::_gui_input); + ADD_SIGNAL(MethodInfo("flag_changed", PropertyInfo(Variant::INT, "flag"))); + } + + EditorPropertyLayersGrid() { + value = 0; + } +}; +void EditorPropertyLayers::_grid_changed(uint32_t p_grid) { + + emit_signal("property_changed", get_edited_property(), p_grid); +} + +void EditorPropertyLayers::update_property() { + + uint32_t value = get_edited_object()->get(get_edited_property()); + + grid->set_flag(value); +} + +void EditorPropertyLayers::setup(LayerType p_layer_type) { + + String basename; + switch (p_layer_type) { + case LAYER_RENDER_2D: + basename = "layer_names/2d_render"; + break; + case LAYER_PHYSICS_2D: + basename = "layer_names/2d_physics"; + break; + case LAYER_RENDER_3D: + basename = "layer_names/3d_render"; + break; + case LAYER_PHYSICS_3D: + basename = "layer_names/3d_physics"; + break; + } + + Vector<String> names; + for (int i = 0; i < 20; i++) { + String name; + + if (ProjectSettings::get_singleton()->has_setting(basename + "/layer_" + itos(i + 1))) { + name = ProjectSettings::get_singleton()->get(basename + "/layer_" + itos(i + 1)); + } + + if (name == "") { + name = "Layer " + itos(i + 1); + } + + names.push_back(name); + } + + grid->names = names; +} + +void EditorPropertyLayers::_button_pressed() { + + layers->clear(); + for (int i = 0; i < 20; i++) { + if (i == 5 || i == 10 || i == 15) { + layers->add_separator(); + } + layers->add_check_item(grid->names[i], i); + int idx = layers->get_item_index(i); + layers->set_item_checked(idx, grid->value & (1 << i)); + } + + Rect2 gp = button->get_global_rect(); + layers->set_as_minsize(); + Vector2 popup_pos = gp.position - Vector2(layers->get_combined_minimum_size().x, 0); + layers->set_global_position(popup_pos); + layers->popup(); +} + +void EditorPropertyLayers::_menu_pressed(int p_menu) { + if (grid->value & (1 << p_menu)) { + grid->value &= ~(1 << p_menu); + } else { + grid->value |= (1 << p_menu); + } + grid->update(); + _grid_changed(grid->value); +} + +void EditorPropertyLayers::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_grid_changed"), &EditorPropertyLayers::_grid_changed); + ClassDB::bind_method(D_METHOD("_button_pressed"), &EditorPropertyLayers::_button_pressed); + ClassDB::bind_method(D_METHOD("_menu_pressed"), &EditorPropertyLayers::_menu_pressed); +} + +EditorPropertyLayers::EditorPropertyLayers() { + + HBoxContainer *hb = memnew(HBoxContainer); + add_child(hb); + grid = memnew(EditorPropertyLayersGrid); + grid->connect("flag_changed", this, "_grid_changed"); + grid->set_h_size_flags(SIZE_EXPAND_FILL); + hb->add_child(grid); + button = memnew(Button); + button->set_text(".."); + button->connect("pressed", this, "_button_pressed"); + hb->add_child(button); + set_bottom_editor(hb); + layers = memnew(PopupMenu); + add_child(layers); + layers->connect("id_pressed", this, "_menu_pressed"); +} +///////////////////// INT ///////////////////////// + +void EditorPropertyInteger::_value_changed(double val) { + if (setting) + return; + emit_signal("property_changed", get_edited_property(), int(val)); +} + +void EditorPropertyInteger::update_property() { + int val = get_edited_object()->get(get_edited_property()); + setting = true; + spin->set_value(val); + setting = false; +} + +void EditorPropertyInteger::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyInteger::_value_changed); +} + +void EditorPropertyInteger::setup(int p_min, int p_max, bool p_allow_greater, bool p_allow_lesser) { + spin->set_min(p_min); + spin->set_max(p_max); + spin->set_step(1); + spin->set_allow_greater(p_allow_greater); + spin->set_allow_lesser(p_allow_lesser); +} + +EditorPropertyInteger::EditorPropertyInteger() { + spin = memnew(EditorSpinSlider); + add_child(spin); + add_focusable(spin); + spin->connect("value_changed", this, "_value_changed"); + setting = false; +} + +///////////////////// OBJECT ID ///////////////////////// + +void EditorPropertyObjectID::_edit_pressed() { + + emit_signal("object_id_selected", get_edited_property(), get_edited_object()->get(get_edited_property())); +} + +void EditorPropertyObjectID::update_property() { + String type = base_type; + if (type == "") + type = "Object"; + + String icon_type = type; + if (has_icon(icon_type, "EditorIcons")) { + type = icon_type; + } else { + type = "Object"; + } + + ObjectID id = get_edited_object()->get(get_edited_property()); + if (id != 0) { + edit->set_text(type + " ID: " + itos(id)); + edit->set_disabled(false); + edit->set_icon(get_icon(icon_type, "EditorIcons")); + } else { + edit->set_text(TTR("[Empty]")); + edit->set_disabled(true); + edit->set_icon(Ref<Texture>()); + } +} + +void EditorPropertyObjectID::setup(const String &p_base_type) { + base_type = p_base_type; +} + +void EditorPropertyObjectID::_bind_methods() { + ClassDB::bind_method(D_METHOD("_edit_pressed"), &EditorPropertyObjectID::_edit_pressed); +} + +EditorPropertyObjectID::EditorPropertyObjectID() { + edit = memnew(Button); + add_child(edit); + add_focusable(edit); + edit->connect("pressed", this, "_edit_pressed"); +} + +///////////////////// FLOAT ///////////////////////// + +void EditorPropertyFloat::_value_changed(double val) { + if (setting) + return; + + emit_signal("property_changed", get_edited_property(), val); +} + +void EditorPropertyFloat::update_property() { + double val = get_edited_object()->get(get_edited_property()); + setting = true; + spin->set_value(val); + setting = false; +} + +void EditorPropertyFloat::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyFloat::_value_changed); +} + +void EditorPropertyFloat::setup(double p_min, double p_max, double p_step, bool p_no_slider, bool p_exp_range, bool p_greater, bool p_lesser) { + + spin->set_min(p_min); + spin->set_max(p_max); + spin->set_step(p_step); + spin->set_hide_slider(p_no_slider); + spin->set_exp_ratio(p_exp_range); + spin->set_allow_greater(p_greater); + spin->set_allow_lesser(p_lesser); +} + +EditorPropertyFloat::EditorPropertyFloat() { + spin = memnew(EditorSpinSlider); + add_child(spin); + add_focusable(spin); + spin->connect("value_changed", this, "_value_changed"); + setting = false; +} + +///////////////////// EASING ///////////////////////// + +void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) { + + Ref<InputEventMouseMotion> mm = p_ev; + + if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) { + + float rel = mm->get_relative().x; + if (rel == 0) + return; + + if (flip) + rel = -rel; + + float val = get_edited_object()->get(get_edited_property()); + if (val == 0) + return; + bool sg = val < 0; + val = Math::absf(val); + + val = Math::log(val) / Math::log((float)2.0); + //logspace + val += rel * 0.05; + + val = Math::pow(2.0f, val); + if (sg) + val = -val; + + emit_signal("property_changed", get_edited_property(), val); + easing_draw->update(); + } +} + +void EditorPropertyEasing::_draw_easing() { + + RID ci = easing_draw->get_canvas_item(); + + Size2 s = easing_draw->get_size(); + Rect2 r(Point2(), s); + r = r.grow(3); + get_stylebox("normal", "LineEdit")->draw(ci, r); + + int points = 48; + + float prev = 1.0; + float exp = get_edited_object()->get(get_edited_property()); + + Ref<Font> f = get_font("font", "Label"); + Color color = get_color("font_color", "Label"); + + for (int i = 1; i <= points; i++) { + + float ifl = i / float(points); + float iflp = (i - 1) / float(points); + + float h = 1.0 - Math::ease(ifl, exp); + + if (flip) { + ifl = 1.0 - ifl; + iflp = 1.0 - iflp; + } + + VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(iflp * s.width, prev * s.height), Point2(ifl * s.width, h * s.height), color); + prev = h; + } + + f->draw(ci, Point2(10, 10 + f->get_ascent()), String::num(exp, 2), color); +} + +void EditorPropertyEasing::update_property() { + easing_draw->update(); +} + +void EditorPropertyEasing::_set_preset(float p_val) { + emit_signal("property_changed", get_edited_property(), p_val); + easing_draw->update(); +} + +void EditorPropertyEasing::setup(bool p_full, bool p_flip) { + + flip = p_flip; + if (p_full) { + HBoxContainer *hb2 = memnew(HBoxContainer); + vb->add_child(hb2); + button_out_in = memnew(ToolButton); + button_out_in->set_tooltip(TTR("Out-In")); + button_out_in->set_h_size_flags(SIZE_EXPAND_FILL); + button_out_in->connect("pressed", this, "_set_preset", varray(-0.5)); + hb2->add_child(button_out_in); + + button_in_out = memnew(ToolButton); + button_in_out->set_tooltip(TTR("In")); + button_in_out->set_h_size_flags(SIZE_EXPAND_FILL); + button_in_out->connect("pressed", this, "_set_preset", varray(-2)); + hb2->add_child(button_in_out); + } +} + +void EditorPropertyEasing::_notification(int p_what) { + + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_ENTER_TREE: { + easing_draw->set_custom_minimum_size(Size2(0, get_font("font", "Label")->get_height() * 2)); + button_linear->set_icon(get_icon("CurveLinear", "EditorIcons")); + button_out->set_icon(get_icon("CurveOut", "EditorIcons")); + button_in->set_icon(get_icon("CurveIn", "EditorIcons")); + button_constant->set_icon(get_icon("CurveConstant", "EditorIcons")); + if (button_out_in) + button_out_in->set_icon(get_icon("CurveOutIn", "EditorIcons")); + if (button_in_out) + button_in_out->set_icon(get_icon("CurveInOut", "EditorIcons")); + } break; + } +} + +void EditorPropertyEasing::_bind_methods() { + + ClassDB::bind_method("_draw_easing", &EditorPropertyEasing::_draw_easing); + ClassDB::bind_method("_drag_easing", &EditorPropertyEasing::_drag_easing); + ClassDB::bind_method("_set_preset", &EditorPropertyEasing::_set_preset); +} + +EditorPropertyEasing::EditorPropertyEasing() { + + vb = memnew(VBoxContainer); + add_child(vb); + HBoxContainer *hb = memnew(HBoxContainer); + set_label_reference(hb); + + vb->add_child(hb); + + button_linear = memnew(ToolButton); + button_linear->set_tooltip(TTR("Linear")); + button_linear->set_h_size_flags(SIZE_EXPAND_FILL); + button_linear->connect("pressed", this, "_set_preset", varray(1)); + hb->add_child(button_linear); + + button_constant = memnew(ToolButton); + button_constant->set_tooltip(TTR("Linear")); + button_constant->set_h_size_flags(SIZE_EXPAND_FILL); + button_constant->connect("pressed", this, "_set_preset", varray(0)); + hb->add_child(button_constant); + + button_out = memnew(ToolButton); + button_out->set_tooltip(TTR("Out")); + button_out->set_h_size_flags(SIZE_EXPAND_FILL); + button_out->connect("pressed", this, "_set_preset", varray(0.5)); + hb->add_child(button_out); + + button_in = memnew(ToolButton); + button_in->set_tooltip(TTR("In")); + button_in->set_h_size_flags(SIZE_EXPAND_FILL); + button_in->connect("pressed", this, "_set_preset", varray(2)); + hb->add_child(button_in); + + button_in_out = NULL; + button_out_in = NULL; + + easing_draw = memnew(Control); + easing_draw->connect("draw", this, "_draw_easing"); + easing_draw->connect("gui_input", this, "_drag_easing"); + easing_draw->set_default_cursor_shape(Control::CURSOR_MOVE); + vb->add_child(easing_draw); + + flip = false; +} + +///////////////////// VECTOR2 ///////////////////////// + +void EditorPropertyVector2::_value_changed(double val) { + if (setting) + return; + + Vector2 v2; + v2.x = spin[0]->get_value(); + v2.y = spin[1]->get_value(); + emit_signal("property_changed", get_edited_property(), v2); +} + +void EditorPropertyVector2::update_property() { + Vector2 val = get_edited_object()->get(get_edited_property()); + setting = true; + spin[0]->set_value(val.x); + spin[1]->set_value(val.y); + setting = false; +} + +void EditorPropertyVector2::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyVector2::_value_changed); +} + +void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, bool p_no_slider) { + for (int i = 0; i < 2; i++) { + spin[i]->set_min(p_min); + spin[i]->set_max(p_max); + spin[i]->set_step(p_step); + spin[i]->set_hide_slider(p_no_slider); + } +} + +EditorPropertyVector2::EditorPropertyVector2() { + VBoxContainer *vb = memnew(VBoxContainer); + add_child(vb); + static const char *desc[2] = { "x", "y" }; + for (int i = 0; i < 2; i++) { + spin[i] = memnew(EditorSpinSlider); + spin[i]->set_label(desc[i]); + vb->add_child(spin[i]); + add_focusable(spin[i]); + spin[i]->connect("value_changed", this, "_value_changed"); + } + set_label_reference(spin[0]); //show text and buttons around this + setting = false; +} + +///////////////////// RECT2 ///////////////////////// + +void EditorPropertyRect2::_value_changed(double val) { + if (setting) + return; + + Rect2 r2; + r2.position.x = spin[0]->get_value(); + r2.position.x = spin[1]->get_value(); + r2.size.y = spin[2]->get_value(); + r2.size.y = spin[3]->get_value(); + emit_signal("property_changed", get_edited_property(), r2); +} + +void EditorPropertyRect2::update_property() { + Rect2 val = get_edited_object()->get(get_edited_property()); + setting = true; + spin[0]->set_value(val.position.x); + spin[1]->set_value(val.position.y); + spin[2]->set_value(val.size.x); + spin[3]->set_value(val.size.y); + setting = false; +} + +void EditorPropertyRect2::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyRect2::_value_changed); +} + +void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool p_no_slider) { + for (int i = 0; i < 4; i++) { + spin[i]->set_min(p_min); + spin[i]->set_max(p_max); + spin[i]->set_step(p_step); + spin[i]->set_hide_slider(p_no_slider); + } +} + +EditorPropertyRect2::EditorPropertyRect2() { + VBoxContainer *vb = memnew(VBoxContainer); + add_child(vb); + static const char *desc[4] = { "x", "y", "w", "h" }; + for (int i = 0; i < 4; i++) { + spin[i] = memnew(EditorSpinSlider); + spin[i]->set_label(desc[i]); + vb->add_child(spin[i]); + add_focusable(spin[i]); + spin[i]->connect("value_changed", this, "_value_changed"); + } + set_label_reference(spin[0]); //show text and buttons around this + setting = false; +} +///////////////////// VECTOR3 ///////////////////////// + +void EditorPropertyVector3::_value_changed(double val) { + if (setting) + return; + + Vector3 v3; + v3.x = spin[0]->get_value(); + v3.y = spin[1]->get_value(); + v3.z = spin[2]->get_value(); + emit_signal("property_changed", get_edited_property(), v3); +} + +void EditorPropertyVector3::update_property() { + Vector3 val = get_edited_object()->get(get_edited_property()); + setting = true; + spin[0]->set_value(val.x); + spin[1]->set_value(val.y); + spin[2]->set_value(val.z); + setting = false; +} + +void EditorPropertyVector3::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyVector3::_value_changed); +} + +void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, bool p_no_slider) { + for (int i = 0; i < 3; i++) { + spin[i]->set_min(p_min); + spin[i]->set_max(p_max); + spin[i]->set_step(p_step); + spin[i]->set_hide_slider(p_no_slider); + } +} + +EditorPropertyVector3::EditorPropertyVector3() { + VBoxContainer *vb = memnew(VBoxContainer); + add_child(vb); + static const char *desc[3] = { "x", "y", "z" }; + for (int i = 0; i < 3; i++) { + spin[i] = memnew(EditorSpinSlider); + spin[i]->set_label(desc[i]); + vb->add_child(spin[i]); + add_focusable(spin[i]); + spin[i]->connect("value_changed", this, "_value_changed"); + } + set_label_reference(spin[0]); //show text and buttons around this + setting = false; +} +///////////////////// PLANE ///////////////////////// + +void EditorPropertyPlane::_value_changed(double val) { + if (setting) + return; + + Plane p; + p.normal.x = spin[0]->get_value(); + p.normal.y = spin[1]->get_value(); + p.normal.z = spin[2]->get_value(); + p.d = spin[3]->get_value(); + emit_signal("property_changed", get_edited_property(), p); +} + +void EditorPropertyPlane::update_property() { + Plane val = get_edited_object()->get(get_edited_property()); + setting = true; + spin[0]->set_value(val.normal.x); + spin[1]->set_value(val.normal.y); + spin[2]->set_value(val.normal.z); + spin[3]->set_value(val.d); + setting = false; +} + +void EditorPropertyPlane::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyPlane::_value_changed); +} + +void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool p_no_slider) { + for (int i = 0; i < 4; i++) { + spin[i]->set_min(p_min); + spin[i]->set_max(p_max); + spin[i]->set_step(p_step); + spin[i]->set_hide_slider(p_no_slider); + } +} + +EditorPropertyPlane::EditorPropertyPlane() { + VBoxContainer *vb = memnew(VBoxContainer); + add_child(vb); + static const char *desc[4] = { "x", "y", "z", "d" }; + for (int i = 0; i < 4; i++) { + spin[i] = memnew(EditorSpinSlider); + spin[i]->set_label(desc[i]); + vb->add_child(spin[i]); + add_focusable(spin[i]); + spin[i]->connect("value_changed", this, "_value_changed"); + } + set_label_reference(spin[0]); //show text and buttons around this + setting = false; +} + +///////////////////// QUAT ///////////////////////// + +void EditorPropertyQuat::_value_changed(double val) { + if (setting) + return; + + Quat p; + p.x = spin[0]->get_value(); + p.y = spin[1]->get_value(); + p.z = spin[2]->get_value(); + p.w = spin[3]->get_value(); + emit_signal("property_changed", get_edited_property(), p); +} + +void EditorPropertyQuat::update_property() { + Quat val = get_edited_object()->get(get_edited_property()); + setting = true; + spin[0]->set_value(val.x); + spin[1]->set_value(val.y); + spin[2]->set_value(val.z); + spin[3]->set_value(val.w); + setting = false; +} + +void EditorPropertyQuat::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyQuat::_value_changed); +} + +void EditorPropertyQuat::setup(double p_min, double p_max, double p_step, bool p_no_slider) { + for (int i = 0; i < 4; i++) { + spin[i]->set_min(p_min); + spin[i]->set_max(p_max); + spin[i]->set_step(p_step); + spin[i]->set_hide_slider(p_no_slider); + } +} + +EditorPropertyQuat::EditorPropertyQuat() { + VBoxContainer *vb = memnew(VBoxContainer); + add_child(vb); + static const char *desc[4] = { "x", "y", "z", "w" }; + for (int i = 0; i < 4; i++) { + spin[i] = memnew(EditorSpinSlider); + spin[i]->set_label(desc[i]); + vb->add_child(spin[i]); + add_focusable(spin[i]); + spin[i]->connect("value_changed", this, "_value_changed"); + } + set_label_reference(spin[0]); //show text and buttons around this + setting = false; +} + +///////////////////// AABB ///////////////////////// + +void EditorPropertyAABB::_value_changed(double val) { + if (setting) + return; + + AABB p; + p.position.x = spin[0]->get_value(); + p.position.y = spin[1]->get_value(); + p.position.z = spin[2]->get_value(); + p.size.x = spin[3]->get_value(); + p.size.y = spin[4]->get_value(); + p.size.z = spin[5]->get_value(); + + emit_signal("property_changed", get_edited_property(), p); +} + +void EditorPropertyAABB::update_property() { + AABB val = get_edited_object()->get(get_edited_property()); + setting = true; + spin[0]->set_value(val.position.x); + spin[1]->set_value(val.position.y); + spin[2]->set_value(val.position.z); + spin[3]->set_value(val.size.x); + spin[4]->set_value(val.size.y); + spin[5]->set_value(val.size.z); + + setting = false; +} + +void EditorPropertyAABB::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyAABB::_value_changed); +} + +void EditorPropertyAABB::setup(double p_min, double p_max, double p_step, bool p_no_slider) { + for (int i = 0; i < 6; i++) { + spin[i]->set_min(p_min); + spin[i]->set_max(p_max); + spin[i]->set_step(p_step); + spin[i]->set_hide_slider(p_no_slider); + } +} + +EditorPropertyAABB::EditorPropertyAABB() { + GridContainer *g = memnew(GridContainer); + g->set_columns(3); + add_child(g); + + static const char *desc[6] = { "x", "y", "z", "w", "h", "d" }; + for (int i = 0; i < 6; i++) { + spin[i] = memnew(EditorSpinSlider); + spin[i]->set_label(desc[i]); + g->add_child(spin[i]); + spin[i]->set_h_size_flags(SIZE_EXPAND_FILL); + add_focusable(spin[i]); + spin[i]->connect("value_changed", this, "_value_changed"); + } + set_bottom_editor(g); + setting = false; +} + +///////////////////// TRANSFORM2D ///////////////////////// + +void EditorPropertyTransform2D::_value_changed(double val) { + if (setting) + return; + + Transform2D p; + p[0][0] = spin[0]->get_value(); + p[0][1] = spin[1]->get_value(); + p[1][0] = spin[2]->get_value(); + p[1][1] = spin[3]->get_value(); + p[2][0] = spin[4]->get_value(); + p[2][1] = spin[5]->get_value(); + + emit_signal("property_changed", get_edited_property(), p); +} + +void EditorPropertyTransform2D::update_property() { + Transform2D val = get_edited_object()->get(get_edited_property()); + setting = true; + spin[0]->set_value(val[0][0]); + spin[1]->set_value(val[0][1]); + spin[2]->set_value(val[1][0]); + spin[3]->set_value(val[1][1]); + spin[4]->set_value(val[2][0]); + spin[5]->set_value(val[2][1]); + + setting = false; +} + +void EditorPropertyTransform2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyTransform2D::_value_changed); +} + +void EditorPropertyTransform2D::setup(double p_min, double p_max, double p_step, bool p_no_slider) { + for (int i = 0; i < 6; i++) { + spin[i]->set_min(p_min); + spin[i]->set_max(p_max); + spin[i]->set_step(p_step); + spin[i]->set_hide_slider(p_no_slider); + } +} + +EditorPropertyTransform2D::EditorPropertyTransform2D() { + GridContainer *g = memnew(GridContainer); + g->set_columns(2); + add_child(g); + + static const char *desc[6] = { "xx", "xy", "yx", "yy", "ox", "oy" }; + for (int i = 0; i < 6; i++) { + spin[i] = memnew(EditorSpinSlider); + spin[i]->set_label(desc[i]); + g->add_child(spin[i]); + spin[i]->set_h_size_flags(SIZE_EXPAND_FILL); + add_focusable(spin[i]); + spin[i]->connect("value_changed", this, "_value_changed"); + } + set_bottom_editor(g); + setting = false; +} + +///////////////////// BASIS ///////////////////////// + +void EditorPropertyBasis::_value_changed(double val) { + if (setting) + return; + + Basis p; + p[0][0] = spin[0]->get_value(); + p[1][0] = spin[1]->get_value(); + p[2][0] = spin[2]->get_value(); + p[0][1] = spin[3]->get_value(); + p[1][1] = spin[4]->get_value(); + p[2][1] = spin[5]->get_value(); + p[0][2] = spin[6]->get_value(); + p[1][2] = spin[7]->get_value(); + p[2][2] = spin[8]->get_value(); + + emit_signal("property_changed", get_edited_property(), p); +} + +void EditorPropertyBasis::update_property() { + Basis val = get_edited_object()->get(get_edited_property()); + setting = true; + spin[0]->set_value(val[0][0]); + spin[1]->set_value(val[1][0]); + spin[2]->set_value(val[2][0]); + spin[3]->set_value(val[0][1]); + spin[4]->set_value(val[1][1]); + spin[5]->set_value(val[2][1]); + spin[6]->set_value(val[0][2]); + spin[7]->set_value(val[1][2]); + spin[8]->set_value(val[2][2]); + + setting = false; +} + +void EditorPropertyBasis::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyBasis::_value_changed); +} + +void EditorPropertyBasis::setup(double p_min, double p_max, double p_step, bool p_no_slider) { + for (int i = 0; i < 9; i++) { + spin[i]->set_min(p_min); + spin[i]->set_max(p_max); + spin[i]->set_step(p_step); + spin[i]->set_hide_slider(p_no_slider); + } +} + +EditorPropertyBasis::EditorPropertyBasis() { + GridContainer *g = memnew(GridContainer); + g->set_columns(3); + add_child(g); + + static const char *desc[9] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz" }; + for (int i = 0; i < 9; i++) { + spin[i] = memnew(EditorSpinSlider); + spin[i]->set_label(desc[i]); + g->add_child(spin[i]); + spin[i]->set_h_size_flags(SIZE_EXPAND_FILL); + add_focusable(spin[i]); + spin[i]->connect("value_changed", this, "_value_changed"); + } + set_bottom_editor(g); + setting = false; +} + +///////////////////// TRANSFORM ///////////////////////// + +void EditorPropertyTransform::_value_changed(double val) { + if (setting) + return; + + Transform p; + p.basis[0][0] = spin[0]->get_value(); + p.basis[1][0] = spin[1]->get_value(); + p.basis[2][0] = spin[2]->get_value(); + p.basis[0][1] = spin[3]->get_value(); + p.basis[1][1] = spin[4]->get_value(); + p.basis[2][1] = spin[5]->get_value(); + p.basis[0][2] = spin[6]->get_value(); + p.basis[1][2] = spin[7]->get_value(); + p.basis[2][2] = spin[8]->get_value(); + p.origin[0] = spin[9]->get_value(); + p.origin[1] = spin[10]->get_value(); + p.origin[2] = spin[11]->get_value(); + + emit_signal("property_changed", get_edited_property(), p); +} + +void EditorPropertyTransform::update_property() { + Transform val = get_edited_object()->get(get_edited_property()); + setting = true; + spin[0]->set_value(val.basis[0][0]); + spin[1]->set_value(val.basis[1][0]); + spin[2]->set_value(val.basis[2][0]); + spin[3]->set_value(val.basis[0][1]); + spin[4]->set_value(val.basis[1][1]); + spin[5]->set_value(val.basis[2][1]); + spin[6]->set_value(val.basis[0][2]); + spin[7]->set_value(val.basis[1][2]); + spin[8]->set_value(val.basis[2][2]); + spin[9]->set_value(val.origin[0]); + spin[10]->set_value(val.origin[1]); + spin[11]->set_value(val.origin[2]); + + setting = false; +} + +void EditorPropertyTransform::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyTransform::_value_changed); +} + +void EditorPropertyTransform::setup(double p_min, double p_max, double p_step, bool p_no_slider) { + for (int i = 0; i < 12; i++) { + spin[i]->set_min(p_min); + spin[i]->set_max(p_max); + spin[i]->set_step(p_step); + spin[i]->set_hide_slider(p_no_slider); + } +} + +EditorPropertyTransform::EditorPropertyTransform() { + GridContainer *g = memnew(GridContainer); + g->set_columns(3); + add_child(g); + + static const char *desc[12] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz", "ox", "oy", "oz" }; + for (int i = 0; i < 12; i++) { + spin[i] = memnew(EditorSpinSlider); + spin[i]->set_label(desc[i]); + g->add_child(spin[i]); + spin[i]->set_h_size_flags(SIZE_EXPAND_FILL); + add_focusable(spin[i]); + spin[i]->connect("value_changed", this, "_value_changed"); + } + set_bottom_editor(g); + setting = false; +} + +////////////// COLOR PICKER ////////////////////// + +void EditorPropertyColor::_color_changed(const Color &p_color) { + + emit_signal("property_changed", get_edited_property(), p_color); +} + +void EditorPropertyColor::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_color_changed"), &EditorPropertyColor::_color_changed); +} + +void EditorPropertyColor::update_property() { + + picker->set_pick_color(get_edited_object()->get(get_edited_property())); +} + +void EditorPropertyColor::setup(bool p_show_alpha) { + picker->set_edit_alpha(p_show_alpha); +} + +EditorPropertyColor::EditorPropertyColor() { + + picker = memnew(ColorPickerButton); + add_child(picker); + picker->set_flat(true); + picker->connect("color_changed", this, "_color_changed"); +} + +////////////// NODE PATH ////////////////////// + +void EditorPropertyNodePath::_node_selected(const NodePath &p_path) { + + Node *base_node = Object::cast_to<Node>(get_edited_object()); + emit_signal("property_changed", get_edited_property(), base_node->get_path().rel_path_to(p_path)); + update_property(); +} + +void EditorPropertyNodePath::_node_assign() { + if (!scene_tree) { + scene_tree = memnew(SceneTreeDialog); + add_child(scene_tree); + scene_tree->connect("selected", this, "_node_selected"); + } + scene_tree->popup_centered_ratio(); +} + +void EditorPropertyNodePath::_node_clear() { + + emit_signal("property_changed", get_edited_property(), NodePath()); + update_property(); +} + +void EditorPropertyNodePath::update_property() { + + NodePath p = get_edited_object()->get(get_edited_property()); + + assign->set_tooltip(p); + if (p == NodePath()) { + assign->set_icon(Ref<Texture>()); + assign->set_text(TTR("Assign..")); + assign->set_flat(false); + return; + } + assign->set_flat(true); + + Node *base_node = NULL; + if (base_hint != NodePath()) { + if (get_tree()->get_root()->has_node(base_hint)) { + base_node = get_tree()->get_root()->get_node(base_hint); + } + } else { + base_node = Object::cast_to<Node>(get_edited_object()); + } + + if (!base_node || !base_node->has_node(p)) { + assign->set_icon(Ref<Texture>()); + assign->set_text(p); + return; + } + + Node *target_node = base_node->get_node(p); + ERR_FAIL_COND(!target_node); + + assign->set_text(target_node->get_name()); + + Ref<Texture> icon; + if (has_icon(target_node->get_class(), "EditorIcons")) + icon = get_icon(target_node->get_class(), "EditorIcons"); + else + icon = get_icon("Node", "EditorIcons"); + + assign->set_icon(icon); +} + +void EditorPropertyNodePath::setup(const NodePath &p_base_hint) { + + base_hint = p_base_hint; +} + +void EditorPropertyNodePath::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + Ref<Texture> t = get_icon("Clear", "EditorIcons"); + clear->set_icon(t); + } +} + +void EditorPropertyNodePath::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_node_selected"), &EditorPropertyNodePath::_node_selected); + ClassDB::bind_method(D_METHOD("_node_assign"), &EditorPropertyNodePath::_node_assign); + ClassDB::bind_method(D_METHOD("_node_clear"), &EditorPropertyNodePath::_node_clear); +} + +EditorPropertyNodePath::EditorPropertyNodePath() { + + HBoxContainer *hbc = memnew(HBoxContainer); + add_child(hbc); + assign = memnew(Button); + assign->set_flat(true); + assign->set_h_size_flags(SIZE_EXPAND_FILL); + assign->set_clip_text(true); + assign->connect("pressed", this, "_node_assign"); + hbc->add_child(assign); + + clear = memnew(Button); + clear->set_flat(true); + clear->connect("pressed", this, "_node_clear"); + hbc->add_child(clear); + + scene_tree = NULL; //do not allocate unnecesarily +} + +////////////// RESOURCE ////////////////////// + +void EditorPropertyResource::_file_selected(const String &p_path) { + + RES res = ResourceLoader::load(p_path); + emit_signal("property_changed", get_edited_property(), res); + update_property(); +} + +void EditorPropertyResource::_menu_option(int p_which) { + + // scene_tree->popup_centered_ratio(); + switch (p_which) { + case OBJ_MENU_LOAD: { + + if (!file) { + file = memnew(EditorFileDialog); + file->connect("file_selected", this, "_file_selected"); + add_child(file); + } + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + String type = base_type; + + List<String> extensions; + for (int i = 0; i < type.get_slice_count(","); i++) { + + ResourceLoader::get_recognized_extensions_for_type(type.get_slice(",", i), &extensions); + } + + Set<String> valid_extensions; + for (List<String>::Element *E = extensions.front(); E; E = E->next()) { + valid_extensions.insert(E->get()); + } + + file->clear_filters(); + for (Set<String>::Element *E = valid_extensions.front(); E; E = E->next()) { + + file->add_filter("*." + E->get() + " ; " + E->get().to_upper()); + } + + file->popup_centered_ratio(); + } break; + + case OBJ_MENU_EDIT: { + + RES res = get_edited_object()->get(get_edited_property()); + + if (!res.is_null()) { + + emit_signal("resource_selected", get_edited_property(), res); + } + } break; + case OBJ_MENU_CLEAR: { + + emit_signal("property_changed", get_edited_property(), RES()); + update_property(); + + } break; + + case OBJ_MENU_MAKE_UNIQUE: { + + RES res_orig = get_edited_object()->get(get_edited_property()); + if (res_orig.is_null()) + return; + + List<PropertyInfo> property_list; + res_orig->get_property_list(&property_list); + List<Pair<String, Variant> > propvalues; + + for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) { + + Pair<String, Variant> p; + PropertyInfo &pi = E->get(); + if (pi.usage & PROPERTY_USAGE_STORAGE) { + + p.first = pi.name; + p.second = res_orig->get(pi.name); + } + + propvalues.push_back(p); + } + + String orig_type = res_orig->get_class(); + + Object *inst = ClassDB::instance(orig_type); + + Ref<Resource> res = Ref<Resource>(Object::cast_to<Resource>(inst)); + + ERR_FAIL_COND(res.is_null()); + + for (List<Pair<String, Variant> >::Element *E = propvalues.front(); E; E = E->next()) { + + Pair<String, Variant> &p = E->get(); + res->set(p.first, p.second); + } + + emit_signal("property_changed", get_edited_property(), res); + update_property(); + + } break; + + case OBJ_MENU_COPY: { + RES res = get_edited_object()->get(get_edited_property()); + + EditorSettings::get_singleton()->set_resource_clipboard(res); + + } break; + case OBJ_MENU_PASTE: { + + RES res = EditorSettings::get_singleton()->get_resource_clipboard(); + emit_signal("property_changed", get_edited_property(), res); + update_property(); + + } break; + case OBJ_MENU_NEW_SCRIPT: { + + if (Object::cast_to<Node>(get_edited_object())) { + EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(get_edited_object())); + } + + } break; + case OBJ_MENU_SHOW_IN_FILE_SYSTEM: { + RES res = get_edited_object()->get(get_edited_property()); + + FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock(); + file_system_dock->navigate_to_path(res->get_path()); + // Ensure that the FileSystem dock is visible. + TabContainer *tab_container = (TabContainer *)file_system_dock->get_parent_control(); + tab_container->set_current_tab(file_system_dock->get_position_in_parent()); + } break; + default: { + + RES res = get_edited_object()->get(get_edited_property()); + + if (p_which >= CONVERT_BASE_ID) { + + int to_type = p_which - CONVERT_BASE_ID; + + Vector<Ref<EditorResourceConversionPlugin> > conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(res); + + ERR_FAIL_INDEX(to_type, conversions.size()); + + Ref<Resource> new_res = conversions[to_type]->convert(res); + + emit_signal("property_changed", get_edited_property(), new_res); + update_property(); + break; + } + ERR_FAIL_COND(inheritors_array.empty()); + + String intype = inheritors_array[p_which - TYPE_BASE_ID]; + + if (intype == "ViewportTexture") { + + if (!scene_tree) { + scene_tree = memnew(SceneTreeDialog); + add_child(scene_tree); + scene_tree->connect("selected", this, "_viewport_selected"); + scene_tree->set_title(TTR("Pick a Viewport")); + } + scene_tree->popup_centered_ratio(); + + return; + } + + Object *obj = ClassDB::instance(intype); + + if (!obj) { + obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource"); + } + + ERR_BREAK(!obj); + Resource *resp = Object::cast_to<Resource>(obj); + ERR_BREAK(!resp); + if (get_edited_object() && base_type != String() && base_type == "Script") { + //make visual script the right type + res->call("set_instance_base_type", get_edited_object()->get_class()); + } + + res = Ref<Resource>(resp); + emit_signal("property_changed", get_edited_property(), res); + update_property(); + + } break; + } +} + +void EditorPropertyResource::_resource_preview(const String &p_path, const Ref<Texture> &p_preview, ObjectID p_obj) { + + RES p = get_edited_object()->get(get_edited_property()); + if (p.is_valid() && p->get_instance_id() == p_obj) { + if (p_preview.is_valid()) { + assign->set_icon(p_preview); + } + } +} + +void EditorPropertyResource::_update_menu() { + //////////////////// UPDATE MENU ////////////////////////// + RES res = get_edited_object()->get(get_edited_property()); + + menu->clear(); + + if (get_edited_property() == "script" && base_type == "Script" && Object::cast_to<Node>(get_edited_object())) { + menu->add_icon_item(get_icon("Script", "EditorIcons"), TTR("New Script"), OBJ_MENU_NEW_SCRIPT); + menu->add_separator(); + } else if (base_type != "") { + int idx = 0; + + Vector<EditorData::CustomType> custom_resources; + + if (EditorNode::get_editor_data().get_custom_types().has("Resource")) { + custom_resources = EditorNode::get_editor_data().get_custom_types()["Resource"]; + } + + for (int i = 0; i < base_type.get_slice_count(","); i++) { + + String base = base_type.get_slice(",", i); + + Set<String> valid_inheritors; + valid_inheritors.insert(base); + List<StringName> inheritors; + ClassDB::get_inheriters_from_class(base.strip_edges(), &inheritors); + + for (int i = 0; i < custom_resources.size(); i++) { + inheritors.push_back(custom_resources[i].name); + } + + List<StringName>::Element *E = inheritors.front(); + while (E) { + valid_inheritors.insert(E->get()); + E = E->next(); + } + + for (Set<String>::Element *E = valid_inheritors.front(); E; E = E->next()) { + String t = E->get(); + + bool is_custom_resource = false; + Ref<Texture> icon; + if (!custom_resources.empty()) { + for (int i = 0; i < custom_resources.size(); i++) { + if (custom_resources[i].name == t) { + is_custom_resource = true; + if (custom_resources[i].icon.is_valid()) + icon = custom_resources[i].icon; + break; + } + } + } + + if (!is_custom_resource && !ClassDB::can_instance(t)) + continue; + + inheritors_array.push_back(t); + + int id = TYPE_BASE_ID + idx; + + if (!icon.is_valid() && has_icon(t, "EditorIcons")) { + icon = get_icon(t, "EditorIcons"); + } + + if (icon.is_valid()) { + + menu->add_icon_item(icon, vformat(TTR("New %s"), t), id); + } else { + + menu->add_item(vformat(TTR("New %s"), t), id); + } + + idx++; + } + } + + if (menu->get_item_count()) + menu->add_separator(); + } + + menu->add_icon_item(get_icon("Load", "EditorIcons"), TTR("Load"), OBJ_MENU_LOAD); + + if (!res.is_null()) { + + menu->add_icon_item(get_icon("Edit", "EditorIcons"), TTR("Edit"), OBJ_MENU_EDIT); + menu->add_icon_item(get_icon("Clear", "EditorIcons"), TTR("Clear"), OBJ_MENU_CLEAR); + menu->add_icon_item(get_icon("Duplicate", "EditorIcons"), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE); + RES r = res; + if (r.is_valid() && r->get_path().is_resource_file()) { + menu->add_separator(); + menu->add_item(TTR("Show in File System"), OBJ_MENU_SHOW_IN_FILE_SYSTEM); + } + } else { + } + + RES cb = EditorSettings::get_singleton()->get_resource_clipboard(); + bool paste_valid = false; + if (cb.is_valid()) { + if (base_type == "") + paste_valid = true; + else + for (int i = 0; i < base_type.get_slice_count(","); i++) + if (ClassDB::is_parent_class(cb->get_class(), base_type.get_slice(",", i))) { + paste_valid = true; + break; + } + } + + if (!res.is_null() || paste_valid) { + menu->add_separator(); + + if (!res.is_null()) { + + menu->add_item(TTR("Copy"), OBJ_MENU_COPY); + } + + if (paste_valid) { + + menu->add_item(TTR("Paste"), OBJ_MENU_PASTE); + } + } + + if (!res.is_null()) { + + Vector<Ref<EditorResourceConversionPlugin> > conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(res); + if (conversions.size()) { + menu->add_separator(); + } + for (int i = 0; i < conversions.size(); i++) { + String what = conversions[i]->converts_to(); + Ref<Texture> icon; + if (has_icon(what, "EditorIcons")) { + + icon = get_icon(what, "EditorIcons"); + } else { + + icon = get_icon(what, "Resource"); + } + + menu->add_icon_item(icon, vformat(TTR("Convert To %s"), what), CONVERT_BASE_ID + i); + } + } + + Rect2 gt = edit->get_global_rect(); + menu->set_as_minsize(); + int ms = menu->get_combined_minimum_size().width; + Vector2 popup_pos = gt.position + gt.size - Vector2(ms, 0); + menu->set_global_position(popup_pos); + menu->popup(); +} + +void EditorPropertyResource::_sub_inspector_property_keyed(const String &p_property, const Variant &p_value, bool) { + + emit_signal("property_keyed_with_value", String(get_edited_property()) + ":" + p_property, p_value); +} + +void EditorPropertyResource::_sub_inspector_resource_selected(const RES &p_resource, const String &p_property) { + + emit_signal("resource_selected", String(get_edited_property()) + ":" + p_property, p_resource); +} + +void EditorPropertyResource::_sub_inspector_object_id_selected(int p_id) { + + emit_signal("object_id_selected", get_edited_property(), p_id); +} + +void EditorPropertyResource::update_property() { + + RES res = get_edited_object()->get(get_edited_property()); + + if (use_sub_inspector) { + + if (res.is_valid() != assign->is_toggle_mode()) { + assign->set_toggle_mode(res.is_valid()); + } +#ifdef TOOLS_ENABLED + if (res.is_valid() && get_edited_object()->editor_is_section_unfolded(get_edited_property())) { + + if (!sub_inspector) { + sub_inspector = memnew(EditorInspector); + sub_inspector->set_enable_v_scroll(false); + + sub_inspector->connect("property_keyed", this, "_sub_inspector_property_keyed"); + sub_inspector->connect("resource_selected", this, "_sub_inspector_resource_selected"); + sub_inspector->connect("object_id_selected", this, "_sub_inspector_object_id_selected"); + sub_inspector->set_keying(is_keying()); + sub_inspector->set_read_only(is_read_only()); + sub_inspector->set_use_folding(is_using_folding()); + + add_child(sub_inspector); + set_bottom_editor(sub_inspector); + assign->set_pressed(true); + } + + if (res.ptr() != sub_inspector->get_edited_object()) { + sub_inspector->edit(res.ptr()); + } + + } else { + if (sub_inspector) { + set_bottom_editor(NULL); + memdelete(sub_inspector); + sub_inspector = NULL; + } + } +#endif + } + + if (res == RES()) { + assign->set_icon(Ref<Texture>()); + assign->set_text(TTR("[empty]")); + } else { + + Ref<Texture> icon; + if (has_icon(res->get_class(), "EditorIcons")) + icon = get_icon(res->get_class(), "EditorIcons"); + else + icon = get_icon("Node", "EditorIcons"); + + assign->set_icon(icon); + + if (res->get_name() != String()) { + assign->set_text(res->get_name()); + } else if (res->get_path().is_resource_file()) { + assign->set_text(res->get_name()); + assign->set_tooltip(res->get_path()); + } else { + assign->set_text(res->get_class()); + } + + if (res->get_path().is_resource_file()) { + assign->set_tooltip(res->get_path()); + } + + //preview will override the above, so called at the end + EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_resource_preview", res->get_instance_id()); + } +} + +void EditorPropertyResource::_resource_selected() { + RES res = get_edited_object()->get(get_edited_property()); + + if (res.is_null()) { + _update_menu(); + return; + } + + if (use_sub_inspector) { + + get_edited_object()->editor_set_section_unfold(get_edited_property(), assign->is_pressed()); + update_property(); + } else { + + emit_signal("resource_selected", get_edited_property(), res); + } +} + +void EditorPropertyResource::setup(const String &p_base_type) { + base_type = p_base_type; +} + +void EditorPropertyResource::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + Ref<Texture> t = get_icon("select_arrow", "Tree"); + edit->set_icon(t); + } + + if (p_what == NOTIFICATION_DRAG_BEGIN) { + + if (is_visible_in_tree()) { + if (_is_drop_valid(get_viewport()->gui_get_drag_data())) { + dropping = true; + assign->update(); + } + } + } + + if (p_what == NOTIFICATION_DRAG_END) { + if (dropping) { + dropping = false; + assign->update(); + } + } +} + +void EditorPropertyResource::_viewport_selected(const NodePath &p_path) { + + Node *to_node = get_node(p_path); + if (!Object::cast_to<Viewport>(to_node)) { + EditorNode::get_singleton()->show_warning(TTR("Selected node is not a Viewport!")); + return; + } + + Ref<ViewportTexture> vt; + vt.instance(); + vt->set_viewport_path_in_scene(get_tree()->get_edited_scene_root()->get_path_to(to_node)); + vt->setup_local_to_scene(); + + emit_signal("property_changed", get_edited_property(), vt); + update_property(); +} + +void EditorPropertyResource::collapse_all_folding() { + if (sub_inspector) { + sub_inspector->collapse_all_folding(); + } +} + +void EditorPropertyResource::expand_all_folding() { + + if (sub_inspector) { + sub_inspector->expand_all_folding(); + } +} + +void EditorPropertyResource::_button_draw() { + + if (dropping) { + Color color = get_color("accent_color", "Editor"); + assign->draw_rect(Rect2(Point2(), assign->get_size()), color, false); + } +} + +Variant EditorPropertyResource::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + + RES res = get_edited_object()->get(get_edited_property()); + if (res.is_valid()) { + + return EditorNode::get_singleton()->drag_resource(res, p_from); + } + + return Variant(); +} + +bool EditorPropertyResource::_is_drop_valid(const Dictionary &p_drag_data) const { + + String allowed_type = base_type; + + Dictionary drag_data = p_drag_data; + if (drag_data.has("type") && String(drag_data["type"]) == "resource") { + Ref<Resource> res = drag_data["resource"]; + for (int i = 0; i < allowed_type.get_slice_count(","); i++) { + String at = allowed_type.get_slice(",", i).strip_edges(); + if (res.is_valid() && ClassDB::is_parent_class(res->get_class(), at)) { + return true; + } + } + } + + if (drag_data.has("type") && String(drag_data["type"]) == "files") { + + Vector<String> files = drag_data["files"]; + + if (files.size() == 1) { + String file = files[0]; + String ftype = EditorFileSystem::get_singleton()->get_file_type(file); + + if (ftype != "") { + + for (int i = 0; i < allowed_type.get_slice_count(","); i++) { + String at = allowed_type.get_slice(",", i).strip_edges(); + if (ClassDB::is_parent_class(ftype, at)) { + return true; + } + } + } + } + } + + return false; +} + +bool EditorPropertyResource::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + + return _is_drop_valid(p_data); +} +void EditorPropertyResource::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + + ERR_FAIL_COND(!_is_drop_valid(p_data)); + + Dictionary drag_data = p_data; + if (drag_data.has("type") && String(drag_data["type"]) == "resource") { + Ref<Resource> res = drag_data["resource"]; + if (res.is_valid()) { + emit_signal("property_changed", get_edited_property(), res); + update_property(); + return; + } + } + + if (drag_data.has("type") && String(drag_data["type"]) == "files") { + + Vector<String> files = drag_data["files"]; + + if (files.size() == 1) { + String file = files[0]; + RES res = ResourceLoader::load(file); + if (res.is_valid()) { + emit_signal("property_changed", get_edited_property(), res); + update_property(); + return; + } + } + } +} + +void EditorPropertyResource::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_file_selected"), &EditorPropertyResource::_file_selected); + ClassDB::bind_method(D_METHOD("_menu_option"), &EditorPropertyResource::_menu_option); + ClassDB::bind_method(D_METHOD("_update_menu"), &EditorPropertyResource::_update_menu); + ClassDB::bind_method(D_METHOD("_resource_preview"), &EditorPropertyResource::_resource_preview); + ClassDB::bind_method(D_METHOD("_resource_selected"), &EditorPropertyResource::_resource_selected); + ClassDB::bind_method(D_METHOD("_viewport_selected"), &EditorPropertyResource::_viewport_selected); + ClassDB::bind_method(D_METHOD("_sub_inspector_property_keyed"), &EditorPropertyResource::_sub_inspector_property_keyed); + ClassDB::bind_method(D_METHOD("_sub_inspector_resource_selected"), &EditorPropertyResource::_sub_inspector_resource_selected); + ClassDB::bind_method(D_METHOD("_sub_inspector_object_id_selected"), &EditorPropertyResource::_sub_inspector_object_id_selected); + ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &EditorPropertyResource::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &EditorPropertyResource::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("drop_data_fw"), &EditorPropertyResource::drop_data_fw); + ClassDB::bind_method(D_METHOD("_button_draw"), &EditorPropertyResource::_button_draw); +} + +EditorPropertyResource::EditorPropertyResource() { + + sub_inspector = NULL; + use_sub_inspector = !bool(EDITOR_GET("interface/inspector/open_resources_in_new_inspector")); + HBoxContainer *hbc = memnew(HBoxContainer); + add_child(hbc); + assign = memnew(Button); + assign->set_flat(true); + assign->set_h_size_flags(SIZE_EXPAND_FILL); + assign->set_clip_text(true); + assign->connect("pressed", this, "_resource_selected"); + assign->set_drag_forwarding(this); + assign->connect("draw", this, "_button_draw"); + hbc->add_child(assign); + + menu = memnew(PopupMenu); + add_child(menu); + edit = memnew(Button); + edit->set_flat(true); + menu->connect("id_pressed", this, "_menu_option"); + edit->connect("pressed", this, "_update_menu"); + hbc->add_child(edit); + + file = NULL; + scene_tree = NULL; + dropping = false; +} + +////////////// DEFAULT PLUGIN ////////////////////// + +bool EditorInspectorDefaultPlugin::can_handle(Object *p_object) { + return true; //can handle everything +} + +void EditorInspectorDefaultPlugin::parse_begin(Object *p_object) { + //do none +} + +bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) { + + switch (p_type) { + + // atomic types + case Variant::NIL: { + EditorPropertyNil *editor = memnew(EditorPropertyNil); + add_property_editor(p_path, editor); + } break; + case Variant::BOOL: { + EditorPropertyCheck *editor = memnew(EditorPropertyCheck); + add_property_editor(p_path, editor); + } break; + case Variant::INT: { + + if (p_hint == PROPERTY_HINT_ENUM) { + EditorPropertyEnum *editor = memnew(EditorPropertyEnum); + Vector<String> options = p_hint_text.split(","); + editor->setup(options); + add_property_editor(p_path, editor); + + } else if (p_hint == PROPERTY_HINT_FLAGS) { + EditorPropertyFlags *editor = memnew(EditorPropertyFlags); + Vector<String> options = p_hint_text.split(","); + editor->setup(options); + add_property_editor(p_path, editor); + + } else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_2D_RENDER || p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_3D_RENDER) { + + EditorPropertyLayers::LayerType lt; + switch (p_hint) { + case PROPERTY_HINT_LAYERS_2D_RENDER: + lt = EditorPropertyLayers::LAYER_RENDER_2D; + break; + case PROPERTY_HINT_LAYERS_2D_PHYSICS: + lt = EditorPropertyLayers::LAYER_PHYSICS_2D; + break; + case PROPERTY_HINT_LAYERS_3D_RENDER: + lt = EditorPropertyLayers::LAYER_RENDER_3D; + break; + case PROPERTY_HINT_LAYERS_3D_PHYSICS: + lt = EditorPropertyLayers::LAYER_PHYSICS_3D; + break; + default: {} //compiler could be smarter here and realize this cant happen + } + EditorPropertyLayers *editor = memnew(EditorPropertyLayers); + editor->setup(lt); + add_property_editor(p_path, editor); + } else if (p_hint == PROPERTY_HINT_OBJECT_ID) { + + EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID); + editor->setup(p_hint_text); + add_property_editor(p_path, editor); + + } else { + EditorPropertyInteger *editor = memnew(EditorPropertyInteger); + int min = 0, max = 65535; + bool greater = true, lesser = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + greater = false; //if using ranged, asume false by default + lesser = false; + min = p_hint_text.get_slice(",", 0).to_int(); + max = p_hint_text.get_slice(",", 1).to_int(); + for (int i = 2; i < p_hint_text.get_slice_count(","); i++) { + String slice = p_hint_text.get_slice(",", i).strip_edges(); + if (slice == "or_greater") { + greater = true; + } + if (slice == "or_lesser") { + lesser = true; + } + } + } + + editor->setup(min, max, greater, lesser); + + add_property_editor(p_path, editor); + } + } break; + case Variant::REAL: { + + if (p_hint == PROPERTY_HINT_EXP_EASING) { + EditorPropertyEasing *editor = memnew(EditorPropertyEasing); + bool full = true; + bool flip = false; + Vector<String> hints = p_hint_text.split(","); + for (int i = 0; i < hints.size(); i++) { + String h = hints[i].strip_edges(); + if (h == "attenuation") { + flip = true; + } + if (h == "inout") { + full = true; + } + } + + editor->setup(full, flip); + add_property_editor(p_path, editor); + + } else { + EditorPropertyFloat *editor = memnew(EditorPropertyFloat); + double min = -65535, max = 65535, step = 0.001; + bool hide_slider = true; + bool exp_range = false; + bool greater = true, lesser = true; + + if ((p_hint == PROPERTY_HINT_RANGE || p_hint == PROPERTY_HINT_EXP_RANGE) && p_hint_text.get_slice_count(",") >= 2) { + greater = false; //if using ranged, asume false by default + lesser = false; + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + if (p_hint_text.get_slice_count(",") >= 3) { + step = p_hint_text.get_slice(",", 2).to_double(); + } + hide_slider = false; + exp_range = p_hint == PROPERTY_HINT_EXP_RANGE; + for (int i = 2; i < p_hint_text.get_slice_count(","); i++) { + String slice = p_hint_text.get_slice(",", i).strip_edges(); + if (slice == "or_greater") { + greater = true; + } + if (slice == "or_lesser") { + lesser = true; + } + } + } + + editor->setup(min, max, step, hide_slider, exp_range, greater, lesser); + + add_property_editor(p_path, editor); + } + } break; + case Variant::STRING: { + + if (p_hint == PROPERTY_HINT_ENUM) { + EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum); + Vector<String> options = p_hint_text.split(","); + editor->setup(options); + add_property_editor(p_path, editor); + } else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) { + EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText); + add_property_editor(p_path, editor); + } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) { + + Vector<String> extensions = p_hint_text.split(","); + bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE; + bool folder = p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_GLOBAL_DIR; + EditorPropertyPath *editor = memnew(EditorPropertyPath); + editor->setup(extensions, folder, global); + add_property_editor(p_path, editor); + } else if (p_hint == PROPERTY_HINT_METHOD_OF_VARIANT_TYPE || + p_hint == PROPERTY_HINT_METHOD_OF_BASE_TYPE || + p_hint == PROPERTY_HINT_METHOD_OF_INSTANCE || + p_hint == PROPERTY_HINT_METHOD_OF_SCRIPT || + p_hint == PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE || + p_hint == PROPERTY_HINT_PROPERTY_OF_BASE_TYPE || + p_hint == PROPERTY_HINT_PROPERTY_OF_INSTANCE || + p_hint == PROPERTY_HINT_PROPERTY_OF_SCRIPT) { + + EditorPropertyMember *editor = memnew(EditorPropertyMember); + + EditorPropertyMember::Type type = EditorPropertyMember::MEMBER_METHOD_OF_BASE_TYPE; + switch (p_hint) { + case PROPERTY_HINT_METHOD_OF_BASE_TYPE: type = EditorPropertyMember::MEMBER_METHOD_OF_BASE_TYPE; break; + case PROPERTY_HINT_METHOD_OF_INSTANCE: type = EditorPropertyMember::MEMBER_METHOD_OF_INSTANCE; break; + case PROPERTY_HINT_METHOD_OF_SCRIPT: type = EditorPropertyMember::MEMBER_METHOD_OF_SCRIPT; break; + case PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE: type = EditorPropertyMember::MEMBER_PROPERTY_OF_VARIANT_TYPE; break; + case PROPERTY_HINT_PROPERTY_OF_BASE_TYPE: type = EditorPropertyMember::MEMBER_PROPERTY_OF_BASE_TYPE; break; + case PROPERTY_HINT_PROPERTY_OF_INSTANCE: type = EditorPropertyMember::MEMBER_PROPERTY_OF_INSTANCE; break; + case PROPERTY_HINT_PROPERTY_OF_SCRIPT: type = EditorPropertyMember::MEMBER_PROPERTY_OF_SCRIPT; break; + default: {} + } + editor->setup(type, p_hint_text); + add_property_editor(p_path, editor); + + } else { + + EditorPropertyText *editor = memnew(EditorPropertyText); + add_property_editor(p_path, editor); + } + } break; + + // math types + + case Variant::VECTOR2: { + EditorPropertyVector2 *editor = memnew(EditorPropertyVector2); + double min = -65535, max = 65535, step = 0.001; + bool hide_slider = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + if (p_hint_text.get_slice_count(",") >= 3) { + step = p_hint_text.get_slice(",", 2).to_double(); + } + hide_slider = false; + } + + editor->setup(min, max, step, hide_slider); + add_property_editor(p_path, editor); + + } break; // 5 + case Variant::RECT2: { + EditorPropertyRect2 *editor = memnew(EditorPropertyRect2); + double min = -65535, max = 65535, step = 0.001; + bool hide_slider = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + if (p_hint_text.get_slice_count(",") >= 3) { + step = p_hint_text.get_slice(",", 2).to_double(); + } + hide_slider = false; + } + + editor->setup(min, max, step, hide_slider); + add_property_editor(p_path, editor); + } break; + case Variant::VECTOR3: { + EditorPropertyVector3 *editor = memnew(EditorPropertyVector3); + double min = -65535, max = 65535, step = 0.001; + bool hide_slider = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + if (p_hint_text.get_slice_count(",") >= 3) { + step = p_hint_text.get_slice(",", 2).to_double(); + } + hide_slider = false; + } + + editor->setup(min, max, step, hide_slider); + add_property_editor(p_path, editor); + + } break; + case Variant::TRANSFORM2D: { + EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D); + double min = -65535, max = 65535, step = 0.001; + bool hide_slider = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + if (p_hint_text.get_slice_count(",") >= 3) { + step = p_hint_text.get_slice(",", 2).to_double(); + } + hide_slider = false; + } + + editor->setup(min, max, step, hide_slider); + add_property_editor(p_path, editor); + + } break; + case Variant::PLANE: { + EditorPropertyPlane *editor = memnew(EditorPropertyPlane); + double min = -65535, max = 65535, step = 0.001; + bool hide_slider = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + if (p_hint_text.get_slice_count(",") >= 3) { + step = p_hint_text.get_slice(",", 2).to_double(); + } + hide_slider = false; + } + + editor->setup(min, max, step, hide_slider); + add_property_editor(p_path, editor); + } break; + case Variant::QUAT: { + EditorPropertyQuat *editor = memnew(EditorPropertyQuat); + double min = -65535, max = 65535, step = 0.001; + bool hide_slider = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + if (p_hint_text.get_slice_count(",") >= 3) { + step = p_hint_text.get_slice(",", 2).to_double(); + } + hide_slider = false; + } + + editor->setup(min, max, step, hide_slider); + add_property_editor(p_path, editor); + } break; // 10 + case Variant::AABB: { + EditorPropertyAABB *editor = memnew(EditorPropertyAABB); + double min = -65535, max = 65535, step = 0.001; + bool hide_slider = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + if (p_hint_text.get_slice_count(",") >= 3) { + step = p_hint_text.get_slice(",", 2).to_double(); + } + hide_slider = false; + } + + editor->setup(min, max, step, hide_slider); + add_property_editor(p_path, editor); + } break; + case Variant::BASIS: { + EditorPropertyBasis *editor = memnew(EditorPropertyBasis); + double min = -65535, max = 65535, step = 0.001; + bool hide_slider = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + if (p_hint_text.get_slice_count(",") >= 3) { + step = p_hint_text.get_slice(",", 2).to_double(); + } + hide_slider = false; + } + + editor->setup(min, max, step, hide_slider); + add_property_editor(p_path, editor); + } break; + case Variant::TRANSFORM: { + EditorPropertyTransform *editor = memnew(EditorPropertyTransform); + double min = -65535, max = 65535, step = 0.001; + bool hide_slider = true; + + if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { + min = p_hint_text.get_slice(",", 0).to_double(); + max = p_hint_text.get_slice(",", 1).to_double(); + if (p_hint_text.get_slice_count(",") >= 3) { + step = p_hint_text.get_slice(",", 2).to_double(); + } + hide_slider = false; + } + + editor->setup(min, max, step, hide_slider); + add_property_editor(p_path, editor); + + } break; + + // misc types + case Variant::COLOR: { + EditorPropertyColor *editor = memnew(EditorPropertyColor); + editor->setup(p_hint != PROPERTY_HINT_COLOR_NO_ALPHA); + add_property_editor(p_path, editor); + } break; + case Variant::NODE_PATH: { + + EditorPropertyNodePath *editor = memnew(EditorPropertyNodePath); + if (p_hint == PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE && p_hint_text != String()) { + editor->setup(p_hint_text); + } + add_property_editor(p_path, editor); + + } break; // 15 + case Variant::_RID: { + } break; + case Variant::OBJECT: { + EditorPropertyResource *editor = memnew(EditorPropertyResource); + editor->setup(p_hint == PROPERTY_HINT_RESOURCE_TYPE ? p_hint_text : "Resource"); + add_property_editor(p_path, editor); + + } break; + case Variant::DICTIONARY: { + EditorPropertyDictionary *editor = memnew(EditorPropertyDictionary); + add_property_editor(p_path, editor); + } break; + case Variant::ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); + } break; + case Variant::POOL_BYTE_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); + } break; // 20 + case Variant::POOL_INT_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); + } break; + case Variant::POOL_REAL_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); + } break; + case Variant::POOL_STRING_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); + } break; + case Variant::POOL_VECTOR2_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); + } break; + case Variant::POOL_VECTOR3_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); + } break; // 25 + case Variant::POOL_COLOR_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); + } break; + default: {} + } + + return false; //can be overriden, although it will most likely be last anyway +} + +void EditorInspectorDefaultPlugin::parse_end() { + //do none +} diff --git a/editor/editor_properties.h b/editor/editor_properties.h new file mode 100644 index 0000000000..03e72b4ec2 --- /dev/null +++ b/editor/editor_properties.h @@ -0,0 +1,546 @@ +/*************************************************************************/ +/* editor_properties.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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_PROPERTIES_H +#define EDITOR_PROPERTIES_H + +#include "editor/create_dialog.h" +#include "editor/editor_file_system.h" +#include "editor/editor_inspector.h" +#include "editor/editor_spin_slider.h" +#include "editor/property_selector.h" +#include "editor/scene_tree_editor.h" +#include "scene/gui/color_picker.h" + +class EditorPropertyNil : public EditorProperty { + GDCLASS(EditorPropertyNil, EditorProperty) + LineEdit *text; + +public: + virtual void update_property(); + EditorPropertyNil(); +}; + +class EditorPropertyText : public EditorProperty { + GDCLASS(EditorPropertyText, EditorProperty) + LineEdit *text; + + bool updating; + void _text_changed(const String &p_string); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + EditorPropertyText(); +}; + +class EditorPropertyMultilineText : public EditorProperty { + GDCLASS(EditorPropertyMultilineText, EditorProperty) + TextEdit *text; + + AcceptDialog *big_text_dialog; + TextEdit *big_text; + Button *open_big_text; + + void _big_text_changed(); + void _text_changed(); + void _open_big_text(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + virtual void update_property(); + EditorPropertyMultilineText(); +}; + +class EditorPropertyTextEnum : public EditorProperty { + GDCLASS(EditorPropertyTextEnum, EditorProperty) + OptionButton *options; + + void _option_selected(int p_which); + +protected: + static void _bind_methods(); + +public: + void setup(const Vector<String> &p_options); + virtual void update_property(); + EditorPropertyTextEnum(); +}; + +class EditorPropertyPath : public EditorProperty { + GDCLASS(EditorPropertyPath, EditorProperty) + Vector<String> extensions; + bool folder; + bool global; + EditorFileDialog *dialog; + Button *path; + + void _path_selected(const String &p_path); + void _path_pressed(); + +protected: + static void _bind_methods(); + +public: + void setup(const Vector<String> &p_extensions, bool p_folder, bool p_global); + virtual void update_property(); + EditorPropertyPath(); +}; + +class EditorPropertyMember : public EditorProperty { + GDCLASS(EditorPropertyMember, EditorProperty) +public: + enum Type { + MEMBER_METHOD_OF_VARIANT_TYPE, ///< a method of a type + MEMBER_METHOD_OF_BASE_TYPE, ///< a method of a base type + MEMBER_METHOD_OF_INSTANCE, ///< a method of an instance + MEMBER_METHOD_OF_SCRIPT, ///< a method of a script & base + MEMBER_PROPERTY_OF_VARIANT_TYPE, ///< a property of a type + MEMBER_PROPERTY_OF_BASE_TYPE, ///< a property of a base type + MEMBER_PROPERTY_OF_INSTANCE, ///< a property of an instance + MEMBER_PROPERTY_OF_SCRIPT, ///< a property of a script & base + + }; + +private: + Type hint; + PropertySelector *selector; + Button *property; + String hint_text; + + void _property_selected(const String &p_selected); + void _property_select(); + +protected: + static void _bind_methods(); + +public: + void setup(Type p_hint, const String &p_hint_text); + virtual void update_property(); + EditorPropertyMember(); +}; + +class EditorPropertyCheck : public EditorProperty { + GDCLASS(EditorPropertyCheck, EditorProperty) + CheckBox *checkbox; + + void _checkbox_pressed(); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + EditorPropertyCheck(); +}; + +class EditorPropertyEnum : public EditorProperty { + GDCLASS(EditorPropertyEnum, EditorProperty) + OptionButton *options; + + void _option_selected(int p_which); + +protected: + static void _bind_methods(); + +public: + void setup(const Vector<String> &p_options); + virtual void update_property(); + EditorPropertyEnum(); +}; + +class EditorPropertyFlags : public EditorProperty { + GDCLASS(EditorPropertyFlags, EditorProperty) + VBoxContainer *vbox; + Vector<CheckBox *> flags; + Vector<int> flag_indices; + + void _flag_toggled(); + +protected: + static void _bind_methods(); + +public: + void setup(const Vector<String> &p_options); + virtual void update_property(); + EditorPropertyFlags(); +}; + +class EditorPropertyLayersGrid; + +class EditorPropertyLayers : public EditorProperty { + GDCLASS(EditorPropertyLayers, EditorProperty) +public: + enum LayerType { + LAYER_PHYSICS_2D, + LAYER_RENDER_2D, + LAYER_PHYSICS_3D, + LAYER_RENDER_3D, + }; + +private: + EditorPropertyLayersGrid *grid; + void _grid_changed(uint32_t p_grid); + LayerType layer_type; + PopupMenu *layers; + Button *button; + + void _button_pressed(); + void _menu_pressed(int p_menu); + +protected: + static void _bind_methods(); + +public: + void setup(LayerType p_layer_type); + virtual void update_property(); + EditorPropertyLayers(); +}; + +class EditorPropertyInteger : public EditorProperty { + GDCLASS(EditorPropertyInteger, EditorProperty) + EditorSpinSlider *spin; + bool setting; + void _value_changed(double p_val); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(int p_min, int p_max, bool p_allow_greater, bool p_allow_lesser); + EditorPropertyInteger(); +}; + +class EditorPropertyObjectID : public EditorProperty { + GDCLASS(EditorPropertyObjectID, EditorProperty) + Button *edit; + String base_type; + void _edit_pressed(); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(const String &p_base_type); + EditorPropertyObjectID(); +}; + +class EditorPropertyFloat : public EditorProperty { + GDCLASS(EditorPropertyFloat, EditorProperty) + EditorSpinSlider *spin; + bool setting; + void _value_changed(double p_val); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(double p_min, double p_max, double p_step, bool p_no_slider, bool p_exp_range, bool p_greater, bool p_lesser); + EditorPropertyFloat(); +}; + +class EditorPropertyEasing : public EditorProperty { + GDCLASS(EditorPropertyEasing, EditorProperty) + Control *easing_draw; + ToolButton *button_out, *button_in, *button_linear, *button_constant; + ToolButton *button_in_out, *button_out_in; + VBoxContainer *vb; + + bool flip; + + void _drag_easing(const Ref<InputEvent> &p_ev); + void _draw_easing(); + void _notification(int p_what); + void _set_preset(float p_val); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(bool p_full, bool p_flip); + EditorPropertyEasing(); +}; + +class EditorPropertyVector2 : public EditorProperty { + GDCLASS(EditorPropertyVector2, EditorProperty) + EditorSpinSlider *spin[2]; + bool setting; + void _value_changed(double p_val); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(double p_min, double p_max, double p_step, bool p_no_slider); + EditorPropertyVector2(); +}; + +class EditorPropertyRect2 : public EditorProperty { + GDCLASS(EditorPropertyRect2, EditorProperty) + EditorSpinSlider *spin[4]; + bool setting; + void _value_changed(double p_val); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(double p_min, double p_max, double p_step, bool p_no_slider); + EditorPropertyRect2(); +}; + +class EditorPropertyVector3 : public EditorProperty { + GDCLASS(EditorPropertyVector3, EditorProperty) + EditorSpinSlider *spin[3]; + bool setting; + void _value_changed(double p_val); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(double p_min, double p_max, double p_step, bool p_no_slider); + EditorPropertyVector3(); +}; + +class EditorPropertyPlane : public EditorProperty { + GDCLASS(EditorPropertyPlane, EditorProperty) + EditorSpinSlider *spin[4]; + bool setting; + void _value_changed(double p_val); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(double p_min, double p_max, double p_step, bool p_no_slider); + EditorPropertyPlane(); +}; + +class EditorPropertyQuat : public EditorProperty { + GDCLASS(EditorPropertyQuat, EditorProperty) + EditorSpinSlider *spin[4]; + bool setting; + void _value_changed(double p_val); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(double p_min, double p_max, double p_step, bool p_no_slider); + EditorPropertyQuat(); +}; + +class EditorPropertyAABB : public EditorProperty { + GDCLASS(EditorPropertyAABB, EditorProperty) + EditorSpinSlider *spin[6]; + bool setting; + void _value_changed(double p_val); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(double p_min, double p_max, double p_step, bool p_no_slider); + EditorPropertyAABB(); +}; + +class EditorPropertyTransform2D : public EditorProperty { + GDCLASS(EditorPropertyTransform2D, EditorProperty) + EditorSpinSlider *spin[6]; + bool setting; + void _value_changed(double p_val); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(double p_min, double p_max, double p_step, bool p_no_slider); + EditorPropertyTransform2D(); +}; + +class EditorPropertyBasis : public EditorProperty { + GDCLASS(EditorPropertyBasis, EditorProperty) + EditorSpinSlider *spin[9]; + bool setting; + void _value_changed(double p_val); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(double p_min, double p_max, double p_step, bool p_no_slider); + EditorPropertyBasis(); +}; + +class EditorPropertyTransform : public EditorProperty { + GDCLASS(EditorPropertyTransform, EditorProperty) + EditorSpinSlider *spin[12]; + bool setting; + void _value_changed(double p_val); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(double p_min, double p_max, double p_step, bool p_no_slider); + EditorPropertyTransform(); +}; + +class EditorPropertyColor : public EditorProperty { + GDCLASS(EditorPropertyColor, EditorProperty) + ColorPickerButton *picker; + void _color_changed(const Color &p_color); + +protected: + static void _bind_methods(); + +public: + virtual void update_property(); + void setup(bool p_show_alpha); + EditorPropertyColor(); +}; + +class EditorPropertyNodePath : public EditorProperty { + GDCLASS(EditorPropertyNodePath, EditorProperty) + Button *assign; + Button *clear; + SceneTreeDialog *scene_tree; + NodePath base_hint; + + void _node_selected(const NodePath &p_path); + void _node_assign(); + void _node_clear(); + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + virtual void update_property(); + void setup(const NodePath &p_base_hint); + EditorPropertyNodePath(); +}; + +class EditorPropertyResource : public EditorProperty { + GDCLASS(EditorPropertyResource, EditorProperty) + + enum MenuOption { + + OBJ_MENU_LOAD = 0, + OBJ_MENU_EDIT = 1, + OBJ_MENU_CLEAR = 2, + OBJ_MENU_MAKE_UNIQUE = 3, + OBJ_MENU_COPY = 4, + OBJ_MENU_PASTE = 5, + OBJ_MENU_NEW_SCRIPT = 6, + OBJ_MENU_SHOW_IN_FILE_SYSTEM = 7, + TYPE_BASE_ID = 100, + CONVERT_BASE_ID = 1000 + + }; + + Button *assign; + Button *edit; + PopupMenu *menu; + EditorFileDialog *file; + Vector<String> inheritors_array; + EditorInspector *sub_inspector; + + bool use_sub_inspector; + bool dropping; + String base_type; + + SceneTreeDialog *scene_tree; + + void _file_selected(const String &p_path); + void _menu_option(int p_which); + void _resource_preview(const String &p_path, const Ref<Texture> &p_preview, ObjectID p_obj); + void _resource_selected(); + void _viewport_selected(const NodePath &p_path); + + void _update_menu(); + + void _sub_inspector_property_keyed(const String &p_property, const Variant &p_value, bool); + void _sub_inspector_resource_selected(const RES &p_resource, const String &p_property); + void _sub_inspector_object_id_selected(int p_id); + + void _button_draw(); + Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); + bool _is_drop_valid(const Dictionary &p_drag_data) const; + bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; + void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + virtual void update_property(); + void setup(const String &p_base_type); + + void collapse_all_folding(); + void expand_all_folding(); + + EditorPropertyResource(); +}; + +/////////////////////////////////////////////////// +/// \brief The EditorInspectorDefaultPlugin class +/// +class EditorInspectorDefaultPlugin : public EditorInspectorPlugin { + GDCLASS(EditorInspectorDefaultPlugin, EditorInspectorPlugin) + +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); + virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage); + virtual void parse_end(); +}; + +#endif // EDITOR_PROPERTIES_H diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp new file mode 100644 index 0000000000..90f8d0e157 --- /dev/null +++ b/editor/editor_properties_array_dict.cpp @@ -0,0 +1,999 @@ +#include "editor_properties_array_dict.h" +#include "editor/editor_scale.h" +#include "editor_properties.h" + +bool EditorPropertyArrayObject::_set(const StringName &p_name, const Variant &p_value) { + + String pn = p_name; + + if (pn.begins_with("indices")) { + int idx = pn.get_slicec('/', 1).to_int(); + array.set(idx, p_value); + return true; + } + + return false; +} + +bool EditorPropertyArrayObject::_get(const StringName &p_name, Variant &r_ret) const { + + String pn = p_name; + + if (pn.begins_with("indices")) { + + int idx = pn.get_slicec('/', 1).to_int(); + bool valid; + r_ret = array.get(idx, &valid); + return valid; + } + + return false; +} + +void EditorPropertyArrayObject::set_array(const Variant &p_array) { + array = p_array; +} + +Variant EditorPropertyArrayObject::get_array() { + return array; +} + +EditorPropertyArrayObject::EditorPropertyArrayObject() { +} + +/////////////////// + +bool EditorPropertyDictionaryObject::_set(const StringName &p_name, const Variant &p_value) { + + String pn = p_name; + + if (pn == "new_item_key") { + + new_item_key = p_value; + return true; + } + + if (pn == "new_item_value") { + + new_item_value = p_value; + return true; + } + + if (pn.begins_with("indices")) { + int idx = pn.get_slicec('/', 1).to_int(); + Variant key = dict.get_key_at_index(idx); + dict[key] = p_value; + return true; + } + + return false; +} + +bool EditorPropertyDictionaryObject::_get(const StringName &p_name, Variant &r_ret) const { + + String pn = p_name; + + if (pn == "new_item_key") { + + r_ret = new_item_key; + return true; + } + + if (pn == "new_item_value") { + + r_ret = new_item_value; + return true; + } + + if (pn.begins_with("indices")) { + + int idx = pn.get_slicec('/', 1).to_int(); + Variant key = dict.get_key_at_index(idx); + r_ret = dict[key]; + return true; + } + + return false; +} + +void EditorPropertyDictionaryObject::set_dict(const Dictionary &p_dict) { + dict = p_dict; +} + +Dictionary EditorPropertyDictionaryObject::get_dict() { + return dict; +} + +void EditorPropertyDictionaryObject::set_new_item_key(const Variant &p_new_item) { + new_item_key = p_new_item; +} + +Variant EditorPropertyDictionaryObject::get_new_item_key() { + return new_item_key; +} + +void EditorPropertyDictionaryObject::set_new_item_value(const Variant &p_new_item) { + new_item_value = p_new_item; +} + +Variant EditorPropertyDictionaryObject::get_new_item_value() { + return new_item_value; +} + +EditorPropertyDictionaryObject::EditorPropertyDictionaryObject() { +} + +///////////////////// ARRAY /////////////////////////// + +void EditorPropertyArray::_property_changed(const String &p_prop, Variant p_value) { + + if (p_prop.begins_with("indices")) { + int idx = p_prop.get_slice("/", 1).to_int(); + Variant array = object->get_array(); + array.set(idx, p_value); + emit_signal("property_changed", get_edited_property(), array); + + if (array.get_type() == Variant::ARRAY) { + array = array.call("duplicate"); //dupe, so undo/redo works better + } + object->set_array(array); + } +} + +void EditorPropertyArray::_change_type(Object *p_button, int p_index) { + + Button *button = Object::cast_to<Button>(p_button); + + Rect2 rect = button->get_global_rect(); + change_type->set_as_minsize(); + change_type->set_global_position(rect.position + rect.size - Vector2(change_type->get_combined_minimum_size().x, 0)); + change_type->popup(); + changing_type_idx = p_index; +} + +void EditorPropertyArray::_change_type_menu(int p_index) { + + Variant value; + Variant::CallError ce; + value = Variant::construct(Variant::Type(p_index), NULL, 0, ce); + Variant array = object->get_array(); + array.set(changing_type_idx, value); + + emit_signal("property_changed", get_edited_property(), array); + + if (array.get_type() == Variant::ARRAY) { + array = array.call("duplicate"); //dupe, so undo/redo works better + } + object->set_array(array); + update_property(); +} + +void EditorPropertyArray::update_property() { + + Variant array = get_edited_object()->get(get_edited_property()); + + if ((!array.is_array()) != edit->is_disabled()) { + + if (array.is_array()) { + edit->set_disabled(false); + edit->set_pressed(false); + + } else { + edit->set_disabled(true); + if (vbox) { + memdelete(vbox); + } + } + } + + if (!array.is_array()) { + return; + } + + String arrtype; + switch (array.get_type()) { + case Variant::ARRAY: { + + arrtype = "Array"; + + } break; + + // arrays + case Variant::POOL_BYTE_ARRAY: { + arrtype = "ByteArray"; + + } break; + case Variant::POOL_INT_ARRAY: { + arrtype = "IntArray"; + + } break; + case Variant::POOL_REAL_ARRAY: { + + arrtype = "FltArray"; + } break; + case Variant::POOL_STRING_ARRAY: { + + arrtype = "StrArray"; + } break; + case Variant::POOL_VECTOR2_ARRAY: { + + arrtype = "Vec2Array"; + } break; + case Variant::POOL_VECTOR3_ARRAY: { + arrtype = "Vec3Array"; + + } break; + case Variant::POOL_COLOR_ARRAY: { + arrtype = "ColArray"; + } break; + default: {} + } + + edit->set_text(arrtype + "[" + itos(array.call("size")) + "]"); + +#ifdef TOOLS_ENABLED + + bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property()); + if (edit->is_pressed() != unfolded) { + edit->set_pressed(unfolded); + } + + if (unfolded) { + + updating = true; + + if (!vbox) { + + vbox = memnew(VBoxContainer); + add_child(vbox); + set_bottom_editor(vbox); + HBoxContainer *hbc = memnew(HBoxContainer); + vbox->add_child(hbc); + Label *label = memnew(Label(TTR("Size: "))); + label->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(label); + length = memnew(EditorSpinSlider); + length->set_step(1); + length->set_max(1000000); + length->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(length); + length->connect("value_changed", this, "_length_changed"); + + page_hb = memnew(HBoxContainer); + vbox->add_child(page_hb); + label = memnew(Label(TTR("Page: "))); + label->set_h_size_flags(SIZE_EXPAND_FILL); + page_hb->add_child(label); + page = memnew(EditorSpinSlider); + page->set_step(1); + page_hb->add_child(page); + page->set_h_size_flags(SIZE_EXPAND_FILL); + page->connect("value_changed", this, "_page_changed"); + } else { + //bye bye children of the box + while (vbox->get_child_count() > 2) { + memdelete(vbox->get_child(2)); + } + } + + int len = array.call("size"); + + length->set_value(len); + + int pages = MAX(0, len - 1) / page_len + 1; + + page->set_max(pages); + page_idx = MIN(page_idx, pages - 1); + page->set_value(page_idx); + page_hb->set_visible(pages > 1); + + int offset = page_idx * page_len; + + int amount = MIN(len - offset, page_len); + + if (array.get_type() == Variant::ARRAY) { + array = array.call("duplicate"); + } + + object->set_array(array); + + for (int i = 0; i < amount; i++) { + String prop_name = "indices/" + itos(i + offset); + + EditorProperty *prop = NULL; + Variant value = array.get(i + offset); + + switch (value.get_type()) { + case Variant::NIL: { + prop = memnew(EditorPropertyNil); + + } break; + + // atomic types + case Variant::BOOL: { + + prop = memnew(EditorPropertyCheck); + + } break; + case Variant::INT: { + EditorPropertyInteger *ed = memnew(EditorPropertyInteger); + ed->setup(-100000, 100000, true, true); + prop = ed; + + } break; + case Variant::REAL: { + + EditorPropertyFloat *ed = memnew(EditorPropertyFloat); + ed->setup(-100000, 100000, 0.001, true, false, true, true); + prop = ed; + } break; + case Variant::STRING: { + + prop = memnew(EditorPropertyText); + + } break; + + // math types + + case Variant::VECTOR2: { + + EditorPropertyVector2 *ed = memnew(EditorPropertyVector2); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::RECT2: { + + EditorPropertyRect2 *ed = memnew(EditorPropertyRect2); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::VECTOR3: { + + EditorPropertyVector3 *ed = memnew(EditorPropertyVector3); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::TRANSFORM2D: { + + EditorPropertyTransform2D *ed = memnew(EditorPropertyTransform2D); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::PLANE: { + + EditorPropertyPlane *ed = memnew(EditorPropertyPlane); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::QUAT: { + + EditorPropertyQuat *ed = memnew(EditorPropertyQuat); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::AABB: { + + EditorPropertyAABB *ed = memnew(EditorPropertyAABB); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::BASIS: { + EditorPropertyBasis *ed = memnew(EditorPropertyBasis); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::TRANSFORM: { + EditorPropertyTransform *ed = memnew(EditorPropertyTransform); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + + // misc types + case Variant::COLOR: { + prop = memnew(EditorPropertyColor); + + } break; + case Variant::NODE_PATH: { + prop = memnew(EditorPropertyNodePath); + + } break; + case Variant::_RID: { + prop = memnew(EditorPropertyNil); + + } break; + case Variant::OBJECT: { + + prop = memnew(EditorPropertyResource); + + } break; + case Variant::DICTIONARY: { + prop = memnew(EditorPropertyDictionary); + + } break; + case Variant::ARRAY: { + + prop = memnew(EditorPropertyArray); + + } break; + + // arrays + case Variant::POOL_BYTE_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + case Variant::POOL_INT_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + case Variant::POOL_REAL_ARRAY: { + + prop = memnew(EditorPropertyArray); + } break; + case Variant::POOL_STRING_ARRAY: { + + prop = memnew(EditorPropertyArray); + } break; + case Variant::POOL_VECTOR2_ARRAY: { + + prop = memnew(EditorPropertyArray); + } break; + case Variant::POOL_VECTOR3_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + case Variant::POOL_COLOR_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + default: {} + } + + prop->set_object_and_property(object.ptr(), prop_name); + prop->set_label(itos(i + offset)); + prop->set_selectable(false); + prop->connect("property_changed", this, "_property_changed"); + if (array.get_type() == Variant::ARRAY) { + HBoxContainer *hb = memnew(HBoxContainer); + vbox->add_child(hb); + hb->add_child(prop); + prop->set_h_size_flags(SIZE_EXPAND_FILL); + Button *edit = memnew(Button); + edit->set_icon(get_icon("Edit", "EditorIcons")); + hb->add_child(edit); + edit->connect("pressed", this, "_change_type", varray(edit, i + offset)); + } else { + vbox->add_child(prop); + } + + prop->update_property(); + } + + updating = false; + + } else { + if (vbox) { + set_bottom_editor(NULL); + memdelete(vbox); + vbox = NULL; + } + } +#endif +} + +void EditorPropertyArray::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + } +} +void EditorPropertyArray::_edit_pressed() { + + get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed()); + update_property(); +} + +void EditorPropertyArray::_page_changed(double p_page) { + if (updating) + return; + page_idx = p_page; + update_property(); +} + +void EditorPropertyArray::_length_changed(double p_page) { + if (updating) + return; + + Variant array = object->get_array(); + array.call("resize", int(p_page)); + emit_signal("property_changed", get_edited_property(), array); + + if (array.get_type() == Variant::ARRAY) { + array = array.call("duplicate"); //dupe, so undo/redo works better + } + object->set_array(array); + update_property(); +} + +void EditorPropertyArray::_bind_methods() { + ClassDB::bind_method("_edit_pressed", &EditorPropertyArray::_edit_pressed); + ClassDB::bind_method("_page_changed", &EditorPropertyArray::_page_changed); + ClassDB::bind_method("_length_changed", &EditorPropertyArray::_length_changed); + ClassDB::bind_method("_property_changed", &EditorPropertyArray::_property_changed); + ClassDB::bind_method("_change_type", &EditorPropertyArray::_change_type); + ClassDB::bind_method("_change_type_menu", &EditorPropertyArray::_change_type_menu); +} + +EditorPropertyArray::EditorPropertyArray() { + + object.instance(); + page_idx = 0; + page_len = 10; + edit = memnew(Button); + edit->set_flat(true); + edit->set_h_size_flags(SIZE_EXPAND_FILL); + edit->set_clip_text(true); + edit->connect("pressed", this, "_edit_pressed"); + edit->set_toggle_mode(true); + add_child(edit); + add_focusable(edit); + vbox = NULL; + page = NULL; + length = NULL; + updating = false; + change_type = memnew(PopupMenu); + add_child(change_type); + change_type->connect("id_pressed", this, "_change_type_menu"); + changing_type_idx = -1; + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + String type = Variant::get_type_name(Variant::Type(i)); + change_type->add_item(type, i); + } + changing_type_idx = -1; +} + +///////////////////// DICTIONARY /////////////////////////// + +void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p_value) { + + if (p_prop == "new_item_key") { + + object->set_new_item_key(p_value); + } else if (p_prop == "new_item_value") { + + object->set_new_item_value(p_value); + } else if (p_prop.begins_with("indices")) { + int idx = p_prop.get_slice("/", 1).to_int(); + Dictionary dict = object->get_dict(); + Variant key = dict.get_key_at_index(idx); + dict[key] = p_value; + + emit_signal("property_changed", get_edited_property(), dict); + + dict = dict.duplicate(); //dupe, so undo/redo works better + object->set_dict(dict); + } +} + +void EditorPropertyDictionary::_change_type(Object *p_button, int p_index) { + + Button *button = Object::cast_to<Button>(p_button); + + Rect2 rect = button->get_global_rect(); + change_type->set_as_minsize(); + change_type->set_global_position(rect.position + rect.size - Vector2(change_type->get_combined_minimum_size().x, 0)); + change_type->popup(); + changing_type_idx = p_index; +} + +void EditorPropertyDictionary::_add_key_value() { + + Dictionary dict = object->get_dict(); + dict[object->get_new_item_key()] = object->get_new_item_value(); + object->set_new_item_key(Variant()); + object->set_new_item_value(Variant()); + + emit_signal("property_changed", get_edited_property(), dict); + + dict = dict.duplicate(); //dupe, so undo/redo works better + object->set_dict(dict); + update_property(); +} + +void EditorPropertyDictionary::_change_type_menu(int p_index) { + + if (changing_type_idx < 0) { + Variant value; + Variant::CallError ce; + value = Variant::construct(Variant::Type(p_index), NULL, 0, ce); + if (changing_type_idx == -1) { + object->set_new_item_key(value); + } else { + object->set_new_item_value(value); + } + update_property(); + return; + } + + Dictionary dict = object->get_dict(); + + if (p_index < Variant::VARIANT_MAX) { + + Variant value; + Variant::CallError ce; + value = Variant::construct(Variant::Type(p_index), NULL, 0, ce); + Variant key = dict.get_key_at_index(changing_type_idx); + dict[key] = value; + } else { + Variant key = dict.get_key_at_index(changing_type_idx); + dict.erase(key); + } + + emit_signal("property_changed", get_edited_property(), dict); + + dict = dict.duplicate(); //dupe, so undo/redo works better + object->set_dict(dict); + update_property(); +} + +void EditorPropertyDictionary::update_property() { + + Dictionary dict = get_edited_object()->get(get_edited_property()); + + edit->set_text("Dict[" + itos(dict.size()) + "]"); + +#ifdef TOOLS_ENABLED + + bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property()); + if (edit->is_pressed() != unfolded) { + edit->set_pressed(unfolded); + } + + if (unfolded) { + + updating = true; + + if (!vbox) { + + vbox = memnew(VBoxContainer); + add_child(vbox); + set_bottom_editor(vbox); + + page_hb = memnew(HBoxContainer); + vbox->add_child(page_hb); + Label *label = memnew(Label(TTR("Page: "))); + label->set_h_size_flags(SIZE_EXPAND_FILL); + page_hb->add_child(label); + page = memnew(EditorSpinSlider); + page->set_step(1); + page_hb->add_child(page); + page->set_h_size_flags(SIZE_EXPAND_FILL); + page->connect("value_changed", this, "_page_changed"); + } else { + //bye bye children of the box + while (vbox->get_child_count() > 1) { + memdelete(vbox->get_child(1)); + } + } + + int len = dict.size(); + + int pages = MAX(0, len - 1) / page_len + 1; + + page->set_max(pages); + page_idx = MIN(page_idx, pages - 1); + page->set_value(page_idx); + page_hb->set_visible(pages > 1); + + int offset = page_idx * page_len; + + int amount = MIN(len - offset, page_len); + + dict = dict.duplicate(); + + object->set_dict(dict); + VBoxContainer *add_vbox = NULL; + + for (int i = 0; i < amount + 2; i++) { + String prop_name; + Variant key; + Variant value; + + if (i < amount) { + prop_name = "indices/" + itos(i + offset); + key = dict.get_key_at_index(i + offset); + value = dict.get_value_at_index(i + offset); + } else if (i == amount) { + prop_name = "new_item_key"; + value = object->get_new_item_key(); + } else if (i == amount + 1) { + prop_name = "new_item_value"; + value = object->get_new_item_value(); + } + + EditorProperty *prop = NULL; + + switch (value.get_type()) { + case Variant::NIL: { + prop = memnew(EditorPropertyNil); + + } break; + + // atomic types + case Variant::BOOL: { + + prop = memnew(EditorPropertyCheck); + + } break; + case Variant::INT: { + EditorPropertyInteger *ed = memnew(EditorPropertyInteger); + ed->setup(-100000, 100000, true, true); + prop = ed; + + } break; + case Variant::REAL: { + + EditorPropertyFloat *ed = memnew(EditorPropertyFloat); + ed->setup(-100000, 100000, 0.001, true, false, true, true); + prop = ed; + } break; + case Variant::STRING: { + + prop = memnew(EditorPropertyText); + + } break; + + // math types + + case Variant::VECTOR2: { + + EditorPropertyVector2 *ed = memnew(EditorPropertyVector2); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::RECT2: { + + EditorPropertyRect2 *ed = memnew(EditorPropertyRect2); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::VECTOR3: { + + EditorPropertyVector3 *ed = memnew(EditorPropertyVector3); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::TRANSFORM2D: { + + EditorPropertyTransform2D *ed = memnew(EditorPropertyTransform2D); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::PLANE: { + + EditorPropertyPlane *ed = memnew(EditorPropertyPlane); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::QUAT: { + + EditorPropertyQuat *ed = memnew(EditorPropertyQuat); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::AABB: { + + EditorPropertyAABB *ed = memnew(EditorPropertyAABB); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::BASIS: { + EditorPropertyBasis *ed = memnew(EditorPropertyBasis); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::TRANSFORM: { + EditorPropertyTransform *ed = memnew(EditorPropertyTransform); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + + // misc types + case Variant::COLOR: { + prop = memnew(EditorPropertyColor); + + } break; + case Variant::NODE_PATH: { + prop = memnew(EditorPropertyNodePath); + + } break; + case Variant::_RID: { + prop = memnew(EditorPropertyNil); + + } break; + case Variant::OBJECT: { + + prop = memnew(EditorPropertyResource); + + } break; + case Variant::DICTIONARY: { + prop = memnew(EditorPropertyDictionary); + + } break; + case Variant::ARRAY: { + + prop = memnew(EditorPropertyArray); + + } break; + + // arrays + case Variant::POOL_BYTE_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + case Variant::POOL_INT_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + case Variant::POOL_REAL_ARRAY: { + + prop = memnew(EditorPropertyArray); + } break; + case Variant::POOL_STRING_ARRAY: { + + prop = memnew(EditorPropertyArray); + } break; + case Variant::POOL_VECTOR2_ARRAY: { + + prop = memnew(EditorPropertyArray); + } break; + case Variant::POOL_VECTOR3_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + case Variant::POOL_COLOR_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + default: {} + } + + if (i == amount) { + PanelContainer *pc = memnew(PanelContainer); + vbox->add_child(pc); + Ref<StyleBoxFlat> flat; + flat.instance(); + for (int j = 0; j < 4; j++) { + flat->set_default_margin(Margin(j), 2 * EDSCALE); + } + flat->set_bg_color(get_color("prop_subsection", "Editor")); + + pc->add_style_override("panel", flat); + add_vbox = memnew(VBoxContainer); + pc->add_child(add_vbox); + } + prop->set_object_and_property(object.ptr(), prop_name); + int change_index; + + if (i < amount) { + String cs = key.get_construct_string(); + prop->set_label(key.get_construct_string()); + prop->set_tooltip(cs); + change_index = i + offset; + } else if (i == amount) { + prop->set_label(TTR("New Key:")); + change_index = -1; + } else if (i == amount + 1) { + prop->set_label(TTR("New Value:")); + change_index = -2; + } + + prop->set_selectable(false); + prop->connect("property_changed", this, "_property_changed"); + + HBoxContainer *hb = memnew(HBoxContainer); + if (add_vbox) { + add_vbox->add_child(hb); + } else { + vbox->add_child(hb); + } + hb->add_child(prop); + prop->set_h_size_flags(SIZE_EXPAND_FILL); + Button *edit = memnew(Button); + edit->set_icon(get_icon("Edit", "EditorIcons")); + hb->add_child(edit); + edit->connect("pressed", this, "_change_type", varray(edit, change_index)); + + prop->update_property(); + + if (i == amount + 1) { + Button *add_item = memnew(Button); + add_item->set_text(TTR("Add Key/Value Pair")); + add_vbox->add_child(add_item); + add_item->connect("pressed", this, "_add_key_value"); + } + } + + updating = false; + + } else { + if (vbox) { + set_bottom_editor(NULL); + memdelete(vbox); + vbox = NULL; + } + } +#endif +} + +void EditorPropertyDictionary::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + } +} +void EditorPropertyDictionary::_edit_pressed() { + + get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed()); + update_property(); +} + +void EditorPropertyDictionary::_page_changed(double p_page) { + if (updating) + return; + page_idx = p_page; + update_property(); +} + +void EditorPropertyDictionary::_bind_methods() { + ClassDB::bind_method("_edit_pressed", &EditorPropertyDictionary::_edit_pressed); + ClassDB::bind_method("_page_changed", &EditorPropertyDictionary::_page_changed); + ClassDB::bind_method("_property_changed", &EditorPropertyDictionary::_property_changed); + ClassDB::bind_method("_change_type", &EditorPropertyDictionary::_change_type); + ClassDB::bind_method("_change_type_menu", &EditorPropertyDictionary::_change_type_menu); + ClassDB::bind_method("_add_key_value", &EditorPropertyDictionary::_add_key_value); +} + +EditorPropertyDictionary::EditorPropertyDictionary() { + + object.instance(); + page_idx = 0; + page_len = 10; + edit = memnew(Button); + edit->set_flat(true); + edit->set_h_size_flags(SIZE_EXPAND_FILL); + edit->set_clip_text(true); + edit->connect("pressed", this, "_edit_pressed"); + edit->set_toggle_mode(true); + add_child(edit); + add_focusable(edit); + vbox = NULL; + page = NULL; + updating = false; + change_type = memnew(PopupMenu); + add_child(change_type); + change_type->connect("id_pressed", this, "_change_type_menu"); + changing_type_idx = -1; + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + String type = Variant::get_type_name(Variant::Type(i)); + change_type->add_item(type, i); + } + change_type->add_separator(); + change_type->add_item(TTR("Remove Item"), Variant::VARIANT_MAX); + changing_type_idx = -1; +} diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h new file mode 100644 index 0000000000..7f6203ee88 --- /dev/null +++ b/editor/editor_properties_array_dict.h @@ -0,0 +1,115 @@ +#ifndef EDITOR_PROPERTIES_ARRAY_DICT_H +#define EDITOR_PROPERTIES_ARRAY_DICT_H + +#include "editor/editor_inspector.h" +#include "editor/editor_spin_slider.h" +#include "scene/gui/button.h" + +class EditorPropertyArrayObject : public Reference { + + GDCLASS(EditorPropertyArrayObject, Reference); + + Variant array; + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + +public: + void set_array(const Variant &p_array); + Variant get_array(); + + EditorPropertyArrayObject(); +}; + +class EditorPropertyDictionaryObject : public Reference { + + GDCLASS(EditorPropertyDictionaryObject, Reference); + + Variant new_item_key; + Variant new_item_value; + Dictionary dict; + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + +public: + void set_dict(const Dictionary &p_dict); + Dictionary get_dict(); + + void set_new_item_key(const Variant &p_new_item); + Variant get_new_item_key(); + + void set_new_item_value(const Variant &p_new_item); + Variant get_new_item_value(); + + EditorPropertyDictionaryObject(); +}; + +class EditorPropertyArray : public EditorProperty { + GDCLASS(EditorPropertyArray, EditorProperty) + + PopupMenu *change_type; + bool updating; + + Ref<EditorPropertyArrayObject> object; + int page_len; + int page_idx; + int changing_type_idx; + Button *edit; + VBoxContainer *vbox; + EditorSpinSlider *length; + EditorSpinSlider *page; + HBoxContainer *page_hb; + + void _page_changed(double p_page); + void _length_changed(double p_page); + void _edit_pressed(); + void _property_changed(const String &p_prop, Variant p_value); + void _change_type(Object *p_button, int p_index); + void _change_type_menu(int p_index); + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + virtual void update_property(); + EditorPropertyArray(); +}; + +class EditorPropertyDictionary : public EditorProperty { + GDCLASS(EditorPropertyDictionary, EditorProperty) + + PopupMenu *change_type; + bool updating; + + Ref<EditorPropertyDictionaryObject> object; + int page_len; + int page_idx; + int changing_type_idx; + Button *edit; + VBoxContainer *vbox; + EditorSpinSlider *length; + EditorSpinSlider *page; + HBoxContainer *page_hb; + + void _page_changed(double p_page); + void _edit_pressed(); + void _property_changed(const String &p_prop, Variant p_value); + void _change_type(Object *p_button, int p_index); + void _change_type_menu(int p_index); + + void _add_key_value(); + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + virtual void update_property(); + EditorPropertyDictionary(); +}; + +#endif // EDITOR_PROPERTIES_ARRAY_DICT_H diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 6eae7be9d5..a47605be15 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -298,8 +298,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { hints["interface/editor/code_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/code_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); _initial_set("interface/editor/main_font", ""); hints["interface/editor/main_font"] = PropertyInfo(Variant::STRING, "interface/editor/main_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); - _initial_set("interface/editor/main__bold_font", ""); - hints["interface/editor/main_font_bold"] = PropertyInfo(Variant::STRING, "interface/editor/main_bold_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + _initial_set("interface/editor/main_font_bold", ""); + hints["interface/editor/main_font_bold"] = PropertyInfo(Variant::STRING, "interface/editor/main_font_bold", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); _initial_set("interface/editor/code_font", ""); hints["interface/editor/code_font"] = PropertyInfo(Variant::STRING, "interface/editor/code_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); _initial_set("interface/editor/dim_editor_on_dialog_popup", true); @@ -371,6 +371,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/line_numbers/line_numbers_zero_padded", false); _initial_set("text_editor/line_numbers/show_breakpoint_gutter", true); _initial_set("text_editor/line_numbers/code_folding", true); + _initial_set("text_editor/line_numbers/word_wrap", false); _initial_set("text_editor/line_numbers/show_line_length_guideline", false); _initial_set("text_editor/line_numbers/line_length_guideline_column", 80); hints["text_editor/line_numbers/line_length_guideline_column"] = PropertyInfo(Variant::INT, "text_editor/line_numbers/line_length_guideline_column", PROPERTY_HINT_RANGE, "20, 160, 1"); @@ -382,6 +383,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/files/trim_trailing_whitespace_on_save", false); _initial_set("text_editor/completion/idle_parse_delay", 2); _initial_set("text_editor/tools/create_signal_callbacks", true); + _initial_set("text_editor/tools/sort_members_outline_alphabetically", false); _initial_set("text_editor/files/autosave_interval_secs", 0); _initial_set("text_editor/cursor/block_caret", false); @@ -403,10 +405,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("editors/grid_map/pick_distance", 5000.0); _initial_set("editors/3d/primary_grid_color", Color::html("909090")); - hints["editors/3d/primary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/primary_grid_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["editors/3d/primary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/primary_grid_color", PROPERTY_HINT_COLOR_NO_ALPHA, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); _initial_set("editors/3d/secondary_grid_color", Color::html("606060")); - hints["editors/3d/secondary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/secondary_grid_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["editors/3d/secondary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/secondary_grid_color", PROPERTY_HINT_COLOR_NO_ALPHA, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); _initial_set("editors/3d/grid_size", 50); hints["editors/3d/grid_size"] = PropertyInfo(Variant::INT, "editors/3d/grid_size", PROPERTY_HINT_RANGE, "1,500,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); @@ -596,6 +598,10 @@ void EditorSettings::_load_default_text_editor_theme() { _initial_set("text_editor/highlighting/word_highlighted_color", Color(0.8, 0.9, 0.9, 0.15)); _initial_set("text_editor/highlighting/search_result_color", Color(0.05, 0.25, 0.05, 1)); _initial_set("text_editor/highlighting/search_result_border_color", Color(0.1, 0.45, 0.1, 1)); + + // GDScript highlighter + _initial_set("text_editor/highlighting/gdscript/function_definition_color", Color::html("#01e1ff")); + _initial_set("text_editor/highlighting/gdscript/node_path_color", Color::html("#64c15a")); } bool EditorSettings::_save_text_editor_theme(String p_file) { @@ -632,6 +638,10 @@ bool EditorSettings::_save_text_editor_theme(String p_file) { cf->set_value(theme_section, "search_result_color", ((Color)get("text_editor/highlighting/search_result_color")).to_html()); cf->set_value(theme_section, "search_result_border_color", ((Color)get("text_editor/highlighting/search_result_border_color")).to_html()); + //GDScript highlighter + cf->set_value(theme_section, "gdscript/function_definition_color", ((Color)get("text_editor/highlighting/gdscript/function_definition_color")).to_html()); + cf->set_value(theme_section, "gdscript/node_path_color", ((Color)get("text_editor/highlighting/gdscript/node_path_color")).to_html()); + Error err = cf->save(p_file); if (err == OK) { @@ -1141,7 +1151,7 @@ void EditorSettings::set_project_metadata(const String &p_section, const String cf->save(path); } -Variant EditorSettings::get_project_metadata(const String &p_section, const String &p_key, Variant p_default) { +Variant EditorSettings::get_project_metadata(const String &p_section, const String &p_key, Variant p_default) const { Ref<ConfigFile> cf = memnew(ConfigFile); String path = get_project_settings_dir().plus_file("project_metadata.cfg"); Error err = cf->load(path); @@ -1483,6 +1493,9 @@ void EditorSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("get_settings_dir"), &EditorSettings::get_settings_dir); ClassDB::bind_method(D_METHOD("get_project_settings_dir"), &EditorSettings::get_project_settings_dir); + ClassDB::bind_method(D_METHOD("set_project_metadata", "section", "key", "data"), &EditorSettings::set_project_metadata); + ClassDB::bind_method(D_METHOD("get_project_metadata", "section", "key", "default"), &EditorSettings::get_project_metadata, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("set_favorite_dirs", "dirs"), &EditorSettings::set_favorite_dirs); ClassDB::bind_method(D_METHOD("get_favorite_dirs"), &EditorSettings::get_favorite_dirs); ClassDB::bind_method(D_METHOD("set_recent_dirs", "dirs"), &EditorSettings::set_recent_dirs); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index e196ca506e..b48aac89c7 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -167,7 +167,7 @@ public: String get_cache_dir() const; void set_project_metadata(const String &p_section, const String &p_key, Variant p_data); - Variant get_project_metadata(const String &p_section, const String &p_key, Variant p_default); + Variant get_project_metadata(const String &p_section, const String &p_key, Variant p_default) const; void set_favorite_dirs(const Vector<String> &p_favorites_dirs); Vector<String> get_favorite_dirs() const; diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp new file mode 100644 index 0000000000..087dcd649f --- /dev/null +++ b/editor/editor_spin_slider.cpp @@ -0,0 +1,345 @@ +/*************************************************************************/ +/* editor_spin_slider.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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_spin_slider.h" +#include "editor_scale.h" +#include "os/input.h" +String EditorSpinSlider::get_text_value() const { + int zeros = Math::step_decimals(get_step()); + return String::num(get_value(), zeros); +} +void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) { + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) { + + if (mb->is_pressed()) { + + if (updown_offset != -1 && mb->get_position().x > updown_offset) { + //there is an updown, so use it. + if (mb->get_position().y < get_size().height / 2) { + set_value(get_value() + get_step()); + } else { + set_value(get_value() - get_step()); + } + return; + } else { + + grabbing_spinner_attempt = true; + grabbing_spinner = false; + grabbing_spinner_mouse_pos = Input::get_singleton()->get_mouse_position(); + } + } else { + + if (grabbing_spinner_attempt) { + + if (grabbing_spinner) { + + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + Input::get_singleton()->warp_mouse_position(grabbing_spinner_mouse_pos); + update(); + } else { + Rect2 gr = get_global_rect(); + value_input->set_text(get_text_value()); + value_input->set_position(gr.position); + value_input->set_size(gr.size); + value_input->call_deferred("show_modal"); + value_input->call_deferred("grab_focus"); + value_input->call_deferred("select_all"); + } + + grabbing_spinner = false; + grabbing_spinner_attempt = false; + } + } + } + + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + + if (grabbing_spinner_attempt) { + + if (!grabbing_spinner) { + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED); + grabbing_spinner = true; + } + + double v = get_value(); + + double diff_x = mm->get_relative().x; + diff_x = Math::pow(ABS(diff_x), 1.8) * SGN(diff_x); + diff_x *= 0.1; + + v += diff_x * get_step(); + + set_value(v); + + } else if (updown_offset != -1) { + bool new_hover = (mm->get_position().x > updown_offset); + if (new_hover != hover_updown) { + hover_updown = new_hover; + update(); + } + } + } + + Ref<InputEventKey> k = p_event; + if (k.is_valid() && k->is_pressed() && k->is_action("ui_accept")) { + Rect2 gr = get_global_rect(); + value_input->set_text(get_text_value()); + value_input->set_position(gr.position); + value_input->set_size(gr.size); + value_input->call_deferred("show_modal"); + value_input->call_deferred("grab_focus"); + value_input->call_deferred("select_all"); + } +} + +void EditorSpinSlider::_value_input_closed() { + set_value(value_input->get_text().to_double()); +} + +void EditorSpinSlider::_value_input_entered(const String &p_text) { + set_value(p_text.to_double()); + value_input->hide(); +} + +void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) { + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) { + + if (mb->is_pressed()) { + + grabbing_grabber = true; + grabbing_ratio = get_as_ratio(); + grabbing_from = grabber->get_transform().xform(mb->get_position()).x; + } else { + grabbing_grabber = false; + } + } + + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid() && grabbing_grabber) { + + float grabbing_ofs = (grabber->get_transform().xform(mm->get_position()).x - grabbing_from) / float(grabber_range); + set_as_ratio(grabbing_ratio + grabbing_ofs); + update(); + } +} + +void EditorSpinSlider::_notification(int p_what) { + + if (p_what == MainLoop::NOTIFICATION_WM_FOCUS_OUT || p_what == MainLoop::NOTIFICATION_WM_FOCUS_OUT) { + if (grabbing_spinner) { + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + grabbing_spinner = false; + grabbing_spinner_attempt = false; + } + } + + if (p_what == NOTIFICATION_DRAW) { + + updown_offset = -1; + + Ref<StyleBox> sb = get_stylebox("normal", "LineEdit"); + draw_style_box(sb, Rect2(Vector2(), get_size())); + Ref<Font> font = get_font("font", "LineEdit"); + + int avail_width = get_size().width - sb->get_minimum_size().width - sb->get_minimum_size().width; + avail_width -= font->get_string_size(label).width; + Ref<Texture> updown = get_icon("updown", "SpinBox"); + + if (get_step() == 1) { + avail_width -= updown->get_width(); + } + + if (has_focus()) { + Ref<StyleBox> focus = get_stylebox("focus", "LineEdit"); + draw_style_box(focus, Rect2(Vector2(), get_size())); + } + + String numstr = get_text_value(); + + int vofs = (get_size().height - font->get_height()) / 2 + font->get_ascent(); + + Color fc = get_color("font_color", "LineEdit"); + + int label_ofs = sb->get_offset().x + avail_width; + draw_string(font, Vector2(label_ofs, vofs), label, fc * Color(1, 1, 1, 0.5)); + draw_string(font, Vector2(sb->get_offset().x, vofs), numstr, fc, avail_width); + + if (get_step() == 1) { + Ref<Texture> updown = get_icon("updown", "SpinBox"); + int updown_vofs = (get_size().height - updown->get_height()) / 2; + updown_offset = get_size().width - sb->get_margin(MARGIN_RIGHT) - updown->get_width(); + Color c(1, 1, 1); + if (hover_updown) { + c *= Color(1.2, 1.2, 1.2); + } + draw_texture(updown, Vector2(updown_offset, updown_vofs), c); + if (grabber->is_visible()) { + grabber->hide(); + } + } else if (!hide_slider) { + int grabber_w = 4 * EDSCALE; + int width = get_size().width - sb->get_minimum_size().width - grabber_w; + int ofs = sb->get_offset().x; + int svofs = (get_size().height + vofs) / 2 - 1; + Color c = fc; + c.a = 0.2; + + draw_rect(Rect2(ofs, svofs + 1, width, 2 * EDSCALE), c); + int gofs = get_as_ratio() * width; + c.a = 0.9; + Rect2 grabber_rect = Rect2(ofs + gofs, svofs + 1, grabber_w, 2 * EDSCALE); + draw_rect(grabber_rect, c); + + bool display_grabber = (mouse_over_spin || mouse_over_grabber) && !grabbing_spinner; + if (grabber->is_visible() != display_grabber) { + if (display_grabber) { + grabber->show(); + } else { + grabber->hide(); + } + } + + if (display_grabber) { + Ref<Texture> grabber_tex; + if (mouse_over_grabber) { + grabber_tex = get_icon("grabber_highlight", "HSlider"); + } else { + grabber_tex = get_icon("grabber", "HSlider"); + } + + if (grabber->get_texture() != grabber_tex) { + grabber->set_texture(grabber_tex); + } + + grabber->set_size(Size2(0, 0)); + grabber->set_position(get_global_position() + grabber_rect.position + grabber_rect.size * 0.5 - grabber->get_size() * 0.5); + grabber_range = width; + } + } + } + + if (p_what == NOTIFICATION_MOUSE_ENTER) { + + mouse_over_spin = true; + update(); + } + if (p_what == NOTIFICATION_MOUSE_EXIT) { + + mouse_over_spin = false; + update(); + } +} + +Size2 EditorSpinSlider::get_minimum_size() const { + + Ref<StyleBox> sb = get_stylebox("normal", "LineEdit"); + Ref<Font> font = get_font("font", "LineEdit"); + + Size2 ms = sb->get_minimum_size(); + ms.height += font->get_height(); + + return ms; +} + +void EditorSpinSlider::set_hide_slider(bool p_hide) { + hide_slider = p_hide; + update(); +} + +bool EditorSpinSlider::is_hiding_slider() const { + return hide_slider; +} + +void EditorSpinSlider::set_label(const String &p_label) { + label = p_label; + update(); +} + +String EditorSpinSlider::get_label() const { + return label; +} + +void EditorSpinSlider::_grabber_mouse_entered() { + mouse_over_grabber = true; + update(); +} + +void EditorSpinSlider::_grabber_mouse_exited() { + mouse_over_grabber = false; + update(); +} + +void EditorSpinSlider::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_label", "label"), &EditorSpinSlider::set_label); + ClassDB::bind_method(D_METHOD("get_label"), &EditorSpinSlider::get_label); + + ClassDB::bind_method(D_METHOD("_gui_input"), &EditorSpinSlider::_gui_input); + ClassDB::bind_method(D_METHOD("_grabber_mouse_entered"), &EditorSpinSlider::_grabber_mouse_entered); + ClassDB::bind_method(D_METHOD("_grabber_mouse_exited"), &EditorSpinSlider::_grabber_mouse_exited); + ClassDB::bind_method(D_METHOD("_grabber_gui_input"), &EditorSpinSlider::_grabber_gui_input); + ClassDB::bind_method(D_METHOD("_value_input_closed"), &EditorSpinSlider::_value_input_closed); + ClassDB::bind_method(D_METHOD("_value_input_entered"), &EditorSpinSlider::_value_input_entered); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "label"), "set_label", "get_label"); +} + +EditorSpinSlider::EditorSpinSlider() { + + grabbing_spinner_attempt = false; + grabbing_spinner = false; + + set_focus_mode(FOCUS_ALL); + updown_offset = -1; + hover_updown = false; + grabber = memnew(TextureRect); + add_child(grabber); + grabber->hide(); + grabber->set_as_toplevel(true); + grabber->set_mouse_filter(MOUSE_FILTER_STOP); + grabber->connect("mouse_entered", this, "_grabber_mouse_entered"); + grabber->connect("mouse_exited", this, "_grabber_mouse_exited"); + grabber->connect("gui_input", this, "_grabber_gui_input"); + mouse_over_spin = false; + mouse_over_grabber = false; + grabbing_grabber = false; + grabber_range = 1; + value_input = memnew(LineEdit); + add_child(value_input); + value_input->set_as_toplevel(true); + value_input->hide(); + value_input->connect("modal_closed", this, "_value_input_closed"); + value_input->connect("text_entered", this, "_value_input_entered"); + hide_slider = false; +} diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h new file mode 100644 index 0000000000..4956990dc2 --- /dev/null +++ b/editor/editor_spin_slider.h @@ -0,0 +1,87 @@ +/*************************************************************************/ +/* editor_spin_slider.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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_SPIN_SLIDER_H +#define EDITOR_SPIN_SLIDER_H + +#include "scene/gui/line_edit.h" +#include "scene/gui/range.h" +#include "scene/gui/texture_rect.h" + +class EditorSpinSlider : public Range { + GDCLASS(EditorSpinSlider, Range) + + String label; + int updown_offset; + bool hover_updown; + bool mouse_hover; + + TextureRect *grabber; + int grabber_range; + + bool mouse_over_spin; + bool mouse_over_grabber; + + bool grabbing_grabber; + int grabbing_from; + float grabbing_ratio; + + bool grabbing_spinner_attempt; + bool grabbing_spinner; + Vector2 grabbing_spinner_mouse_pos; + + LineEdit *value_input; + + void _grabber_gui_input(const Ref<InputEvent> &p_event); + void _value_input_closed(); + void _value_input_entered(const String &); + + bool hide_slider; + +protected: + void _notification(int p_what); + void _gui_input(const Ref<InputEvent> &p_event); + static void _bind_methods(); + void _grabber_mouse_entered(); + void _grabber_mouse_exited(); + +public: + String get_text_value() const; + void set_label(const String &p_label); + String get_label() const; + + void set_hide_slider(bool p_hide); + bool is_hiding_slider() const; + + virtual Size2 get_minimum_size() const; + EditorSpinSlider(); +}; + +#endif // EDITOR_SPIN_SLIDER_H diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index bf7236cc2b..8d29e0d40b 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1047,7 +1047,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { const Color comment_color = dim_color; const Color string_color = Color::html(dark_theme ? "#ffd942" : "#ffd118").linear_interpolate(mono_color, dark_theme ? 0.5 : 0.3); - const Color te_background_color = Color(0, 0, 0, 0); + const Color function_definition_color = Color::html(dark_theme ? "#01e1ff" : "#00a5ba"); + const Color node_path_color = Color::html(dark_theme ? "64c15a" : "#518b4b"); + + const Color te_background_color = dark_theme ? background_color : Color::html("#ffffff"); const Color completion_background_color = base_color; const Color completion_selected_color = alpha1; const Color completion_existing_color = alpha2; @@ -1081,7 +1084,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { setting->set_initial_value("text_editor/highlighting/engine_type_color", type_color, true); setting->set_initial_value("text_editor/highlighting/comment_color", comment_color, true); setting->set_initial_value("text_editor/highlighting/string_color", string_color, true); - setting->set_initial_value("text_editor/highlighting/background_color", background_color, true); + setting->set_initial_value("text_editor/highlighting/background_color", te_background_color, true); setting->set_initial_value("text_editor/highlighting/completion_background_color", completion_background_color, true); setting->set_initial_value("text_editor/highlighting/completion_selected_color", completion_selected_color, true); setting->set_initial_value("text_editor/highlighting/completion_existing_color", completion_existing_color, true); @@ -1105,6 +1108,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { setting->set_initial_value("text_editor/highlighting/code_folding_color", code_folding_color, true); setting->set_initial_value("text_editor/highlighting/search_result_color", search_result_color, true); setting->set_initial_value("text_editor/highlighting/search_result_border_color", search_result_border_color, true); + + setting->set_initial_value("text_editor/highlighting/gdscript/function_definition_color", function_definition_color, true); + setting->set_initial_value("text_editor/highlighting/gdscript/node_path_color", node_path_color, true); } else if (text_editor_color_theme == "Default") { setting->set_initial_value("text_editor/highlighting/symbol_color", Color::html("badfff"), true); setting->set_initial_value("text_editor/highlighting/keyword_color", Color::html("ffffb3"), true); @@ -1112,7 +1118,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { setting->set_initial_value("text_editor/highlighting/engine_type_color", Color::html("83d3ff"), true); setting->set_initial_value("text_editor/highlighting/comment_color", Color::html("676767"), true); setting->set_initial_value("text_editor/highlighting/string_color", Color::html("ef6ebe"), true); - setting->set_initial_value("text_editor/highlighting/background_color", Color::html("3b000000"), true); + setting->set_initial_value("text_editor/highlighting/background_color", dark_theme ? Color::html("3b000000") : Color::html("#323b4f"), true); setting->set_initial_value("text_editor/highlighting/completion_background_color", Color::html("2C2A32"), true); setting->set_initial_value("text_editor/highlighting/completion_selected_color", Color::html("434244"), true); setting->set_initial_value("text_editor/highlighting/completion_existing_color", Color::html("21dfdfdf"), true); @@ -1136,6 +1142,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { setting->set_initial_value("text_editor/highlighting/code_folding_color", Color(0.8, 0.8, 0.8, 0.8), true); setting->set_initial_value("text_editor/highlighting/search_result_color", Color(0.05, 0.25, 0.05, 1), true); setting->set_initial_value("text_editor/highlighting/search_result_border_color", Color(0.1, 0.45, 0.1, 1), true); + + setting->set_initial_value("text_editor/highlighting/gdscript/function_definition_color", Color::html("#01e1ff"), true); + setting->set_initial_value("text_editor/highlighting/gdscript/node_path_color", Color::html("#64c15a"), true); } return theme; diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index e7741c7926..297373d299 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -383,11 +383,11 @@ void FileSystemDock::_update_file_display_toggle_button() { if (button_display_mode->is_pressed()) { display_mode = DISPLAY_LIST; button_display_mode->set_icon(get_icon("FileThumbnail", "EditorIcons")); - button_display_mode->set_tooltip(TTR("View items as a grid of thumbnails")); + button_display_mode->set_tooltip(TTR("View items as a grid of thumbnails.")); } else { display_mode = DISPLAY_THUMBNAILS; button_display_mode->set_icon(get_icon("FileList", "EditorIcons")); - button_display_mode->set_tooltip(TTR("View items as a list")); + button_display_mode->set_tooltip(TTR("View items as a list.")); } } @@ -1860,7 +1860,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { button_favorite->set_flat(true); button_favorite->set_toggle_mode(true); button_favorite->connect("pressed", this, "_favorites_pressed"); - button_favorite->set_tooltip(TTR("Toggle folder status as Favorite")); + button_favorite->set_tooltip(TTR("Toggle folder status as Favorite.")); button_favorite->set_focus_mode(FOCUS_NONE); toolbar_hbc->add_child(button_favorite); @@ -1916,11 +1916,13 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { file_list_vb->add_child(path_hb); button_tree = memnew(ToolButton); + button_tree->set_tooltip(TTR("Enter tree-view.")); button_tree->hide(); path_hb->add_child(button_tree); search_box = memnew(LineEdit); search_box->set_h_size_flags(SIZE_EXPAND_FILL); + search_box->set_placeholder(TTR("Search files")); search_box->connect("text_changed", this, "_search_changed"); path_hb->add_child(search_box); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 74ea46838b..004a49e2b4 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -109,6 +109,7 @@ void FindInFiles::start() { _current_dir = ""; PoolStringArray init_folder; init_folder.append(_root_dir); + _folders_stack.clear(); _folders_stack.push_back(init_folder); _initial_files_count = 0; @@ -127,11 +128,12 @@ void FindInFiles::_process() { // This part can be moved to a thread if needed OS &os = *OS::get_singleton(); - float duration = 0.0; - while (duration < 1.0 / 120.0) { - float time_before = os.get_ticks_msec(); + float time_before = os.get_ticks_msec(); + while (is_processing()) { _iterate(); - duration += (os.get_ticks_msec() - time_before); + float elapsed = (os.get_ticks_msec() - time_before); + if (elapsed > 1000.0 / 120.0) + break; } } @@ -428,6 +430,7 @@ FindInFilesDialog::FindInFilesDialog() { void FindInFilesDialog::set_search_text(String text) { _search_text_line_edit->set_text(text); + _on_search_text_modified(text); } String FindInFilesDialog::get_search_text() const { diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index 8443311a54..e42f9780a6 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -286,6 +286,9 @@ void GroupDialog::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { add_button->set_icon(get_icon("Forward", "EditorIcons")); remove_button->set_icon(get_icon("Back", "EditorIcons")); + + add_filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + remove_filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); } break; } } diff --git a/editor/icons/icon_GUI_slider_grabber.svg b/editor/icons/icon_GUI_slider_grabber.svg index b1dcf980a5..b8e6f0a654 100644 --- a/editor/icons/icon_GUI_slider_grabber.svg +++ b/editor/icons/icon_GUI_slider_grabber.svg @@ -1,5 +1,82 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<circle cx="8" cy="1044.4" r="3" fill="#fff" fill-opacity=".78431" stroke-linejoin="round" stroke-opacity=".39216" stroke-width="3"/> -</g> +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 15.999999" + id="svg8" + sodipodi:docname="icon_GUI_slider_grabber.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + <metadata + id="metadata14"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs12" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1211" + inkscape:window-height="644" + id="namedview10" + showgrid="false" + inkscape:zoom="14.75" + inkscape:cx="-5.7627119" + inkscape:cy="8" + inkscape:window-x="67" + inkscape:window-y="27" + inkscape:window-maximized="0" + inkscape:current-layer="g6" /> + <g + transform="translate(0 -1036.4)" + id="g6"> + <path + transform="translate(0 1036.4)" + d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7 -7 7 7 0 0 0 -7 -7zm0 2a5 5 0 0 1 0.5 0.025391 5 5 0 0 1 0.49414 0.074219 5 5 0 0 1 0.48438 0.12305 5 5 0 0 1 0.46875 0.17188 5 5 0 0 1 0.44922 0.2168 5 5 0 0 1 0.42578 0.26172 5 5 0 0 1 0.39844 0.30273 5 5 0 0 1 0.36524 0.33984 5 5 0 0 1 0.33008 0.37695 5 5 0 0 1 0.29102 0.40625 5 5 0 0 1 0.24805 0.43359 5 5 0 0 1 0.20508 0.45508 5 5 0 0 1 0.1582 0.47461 5 5 0 0 1 0.10938 0.48828 5 5 0 0 1 0.060547 0.49609 5 5 0 0 1 0.011719 0.35352 5 5 0 0 1 -0.025391 0.5 5 5 0 0 1 -0.074218 0.49414 5 5 0 0 1 -0.12305 0.48438 5 5 0 0 1 -0.17188 0.46875 5 5 0 0 1 -0.2168 0.44922 5 5 0 0 1 -0.26172 0.42578 5 5 0 0 1 -0.30273 0.39844 5 5 0 0 1 -0.33984 0.36524 5 5 0 0 1 -0.37695 0.33008 5 5 0 0 1 -0.40625 0.29102 5 5 0 0 1 -0.43359 0.24805 5 5 0 0 1 -0.45508 0.20508 5 5 0 0 1 -0.47461 0.1582 5 5 0 0 1 -0.48828 0.10938 5 5 0 0 1 -0.49609 0.060547 5 5 0 0 1 -0.35352 0.011719 5 5 0 0 1 -0.5 -0.025391 5 5 0 0 1 -0.49414 -0.074218 5 5 0 0 1 -0.48438 -0.12305 5 5 0 0 1 -0.46875 -0.17188 5 5 0 0 1 -0.44922 -0.2168 5 5 0 0 1 -0.42578 -0.26172 5 5 0 0 1 -0.39844 -0.30273 5 5 0 0 1 -0.36523 -0.33984 5 5 0 0 1 -0.33008 -0.37695 5 5 0 0 1 -0.29102 -0.40625 5 5 0 0 1 -0.24805 -0.43359 5 5 0 0 1 -0.20508 -0.45508 5 5 0 0 1 -0.1582 -0.47461 5 5 0 0 1 -0.10938 -0.48828 5 5 0 0 1 -0.060547 -0.49609 5 5 0 0 1 -0.011719 -0.35352 5 5 0 0 1 0.025391 -0.5 5 5 0 0 1 0.074219 -0.49414 5 5 0 0 1 0.12305 -0.48438 5 5 0 0 1 0.17188 -0.46875 5 5 0 0 1 0.2168 -0.44922 5 5 0 0 1 0.26172 -0.42578 5 5 0 0 1 0.30273 -0.39844 5 5 0 0 1 0.33984 -0.36523 5 5 0 0 1 0.37695 -0.33008 5 5 0 0 1 0.40625 -0.29102 5 5 0 0 1 0.43359 -0.24805 5 5 0 0 1 0.45508 -0.20508 5 5 0 0 1 0.47461 -0.1582 5 5 0 0 1 0.48828 -0.10938 5 5 0 0 1 0.49609 -0.060547 5 5 0 0 1 0.35352 -0.011719z" + fill="#e0e0e0" + id="path2" + style="fill:#e0e0e0;fill-opacity:0.28925619" /> + <circle + cx="8" + cy="1044.4" + r="3" + fill="#fff" + fill-opacity=".58824" + stroke-linecap="round" + stroke-linejoin="round" + stroke-opacity=".32549" + stroke-width="3" + id="circle4" /> + </g> + <g + transform="translate(-0.06779632,-1036.4)" + id="g18"> + <circle + style="fill:#ffffff;fill-opacity:0.78430996;stroke-width:3;stroke-linejoin:round;stroke-opacity:0.39216003" + cx="8" + cy="1044.4" + r="3" + id="circle16" /> + </g> </svg> diff --git a/editor/icons/icon_GUI_slider_grabber_hl.svg b/editor/icons/icon_GUI_slider_grabber_hl.svg index 73252751ce..a04ac44cf6 100644 --- a/editor/icons/icon_GUI_slider_grabber_hl.svg +++ b/editor/icons/icon_GUI_slider_grabber_hl.svg @@ -1,6 +1,80 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path transform="translate(0 1036.4)" d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7 -7 7 7 0 0 0 -7 -7zm0 2a5 5 0 0 1 0.5 0.025391 5 5 0 0 1 0.49414 0.074219 5 5 0 0 1 0.48438 0.12305 5 5 0 0 1 0.46875 0.17188 5 5 0 0 1 0.44922 0.2168 5 5 0 0 1 0.42578 0.26172 5 5 0 0 1 0.39844 0.30273 5 5 0 0 1 0.36524 0.33984 5 5 0 0 1 0.33008 0.37695 5 5 0 0 1 0.29102 0.40625 5 5 0 0 1 0.24805 0.43359 5 5 0 0 1 0.20508 0.45508 5 5 0 0 1 0.1582 0.47461 5 5 0 0 1 0.10938 0.48828 5 5 0 0 1 0.060547 0.49609 5 5 0 0 1 0.011719 0.35352 5 5 0 0 1 -0.025391 0.5 5 5 0 0 1 -0.074218 0.49414 5 5 0 0 1 -0.12305 0.48438 5 5 0 0 1 -0.17188 0.46875 5 5 0 0 1 -0.2168 0.44922 5 5 0 0 1 -0.26172 0.42578 5 5 0 0 1 -0.30273 0.39844 5 5 0 0 1 -0.33984 0.36524 5 5 0 0 1 -0.37695 0.33008 5 5 0 0 1 -0.40625 0.29102 5 5 0 0 1 -0.43359 0.24805 5 5 0 0 1 -0.45508 0.20508 5 5 0 0 1 -0.47461 0.1582 5 5 0 0 1 -0.48828 0.10938 5 5 0 0 1 -0.49609 0.060547 5 5 0 0 1 -0.35352 0.011719 5 5 0 0 1 -0.5 -0.025391 5 5 0 0 1 -0.49414 -0.074218 5 5 0 0 1 -0.48438 -0.12305 5 5 0 0 1 -0.46875 -0.17188 5 5 0 0 1 -0.44922 -0.2168 5 5 0 0 1 -0.42578 -0.26172 5 5 0 0 1 -0.39844 -0.30273 5 5 0 0 1 -0.36523 -0.33984 5 5 0 0 1 -0.33008 -0.37695 5 5 0 0 1 -0.29102 -0.40625 5 5 0 0 1 -0.24805 -0.43359 5 5 0 0 1 -0.20508 -0.45508 5 5 0 0 1 -0.1582 -0.47461 5 5 0 0 1 -0.10938 -0.48828 5 5 0 0 1 -0.060547 -0.49609 5 5 0 0 1 -0.011719 -0.35352 5 5 0 0 1 0.025391 -0.5 5 5 0 0 1 0.074219 -0.49414 5 5 0 0 1 0.12305 -0.48438 5 5 0 0 1 0.17188 -0.46875 5 5 0 0 1 0.2168 -0.44922 5 5 0 0 1 0.26172 -0.42578 5 5 0 0 1 0.30273 -0.39844 5 5 0 0 1 0.33984 -0.36523 5 5 0 0 1 0.37695 -0.33008 5 5 0 0 1 0.40625 -0.29102 5 5 0 0 1 0.43359 -0.24805 5 5 0 0 1 0.45508 -0.20508 5 5 0 0 1 0.47461 -0.1582 5 5 0 0 1 0.48828 -0.10938 5 5 0 0 1 0.49609 -0.060547 5 5 0 0 1 0.35352 -0.011719z" fill="#e0e0e0"/> -<circle cx="8" cy="1044.4" r="3" fill="#fff" fill-opacity=".58824" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".32549" stroke-width="3"/> -</g> +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 15.999999" + id="svg8" + sodipodi:docname="icon_GUI_slider_grabber_hl.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + <metadata + id="metadata14"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs12" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="944" + inkscape:window-height="480" + id="namedview10" + showgrid="false" + inkscape:zoom="14.75" + inkscape:cx="8" + inkscape:cy="8" + inkscape:window-x="67" + inkscape:window-y="27" + inkscape:window-maximized="0" + inkscape:current-layer="svg8" /> + <g + transform="translate(0 -1036.4)" + id="g6"> + <path + transform="translate(0 1036.4)" + d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7 -7 7 7 0 0 0 -7 -7zm0 2a5 5 0 0 1 0.5 0.025391 5 5 0 0 1 0.49414 0.074219 5 5 0 0 1 0.48438 0.12305 5 5 0 0 1 0.46875 0.17188 5 5 0 0 1 0.44922 0.2168 5 5 0 0 1 0.42578 0.26172 5 5 0 0 1 0.39844 0.30273 5 5 0 0 1 0.36524 0.33984 5 5 0 0 1 0.33008 0.37695 5 5 0 0 1 0.29102 0.40625 5 5 0 0 1 0.24805 0.43359 5 5 0 0 1 0.20508 0.45508 5 5 0 0 1 0.1582 0.47461 5 5 0 0 1 0.10938 0.48828 5 5 0 0 1 0.060547 0.49609 5 5 0 0 1 0.011719 0.35352 5 5 0 0 1 -0.025391 0.5 5 5 0 0 1 -0.074218 0.49414 5 5 0 0 1 -0.12305 0.48438 5 5 0 0 1 -0.17188 0.46875 5 5 0 0 1 -0.2168 0.44922 5 5 0 0 1 -0.26172 0.42578 5 5 0 0 1 -0.30273 0.39844 5 5 0 0 1 -0.33984 0.36524 5 5 0 0 1 -0.37695 0.33008 5 5 0 0 1 -0.40625 0.29102 5 5 0 0 1 -0.43359 0.24805 5 5 0 0 1 -0.45508 0.20508 5 5 0 0 1 -0.47461 0.1582 5 5 0 0 1 -0.48828 0.10938 5 5 0 0 1 -0.49609 0.060547 5 5 0 0 1 -0.35352 0.011719 5 5 0 0 1 -0.5 -0.025391 5 5 0 0 1 -0.49414 -0.074218 5 5 0 0 1 -0.48438 -0.12305 5 5 0 0 1 -0.46875 -0.17188 5 5 0 0 1 -0.44922 -0.2168 5 5 0 0 1 -0.42578 -0.26172 5 5 0 0 1 -0.39844 -0.30273 5 5 0 0 1 -0.36523 -0.33984 5 5 0 0 1 -0.33008 -0.37695 5 5 0 0 1 -0.29102 -0.40625 5 5 0 0 1 -0.24805 -0.43359 5 5 0 0 1 -0.20508 -0.45508 5 5 0 0 1 -0.1582 -0.47461 5 5 0 0 1 -0.10938 -0.48828 5 5 0 0 1 -0.060547 -0.49609 5 5 0 0 1 -0.011719 -0.35352 5 5 0 0 1 0.025391 -0.5 5 5 0 0 1 0.074219 -0.49414 5 5 0 0 1 0.12305 -0.48438 5 5 0 0 1 0.17188 -0.46875 5 5 0 0 1 0.2168 -0.44922 5 5 0 0 1 0.26172 -0.42578 5 5 0 0 1 0.30273 -0.39844 5 5 0 0 1 0.33984 -0.36523 5 5 0 0 1 0.37695 -0.33008 5 5 0 0 1 0.40625 -0.29102 5 5 0 0 1 0.43359 -0.24805 5 5 0 0 1 0.45508 -0.20508 5 5 0 0 1 0.47461 -0.1582 5 5 0 0 1 0.48828 -0.10938 5 5 0 0 1 0.49609 -0.060547 5 5 0 0 1 0.35352 -0.011719z" + fill="#e0e0e0" + id="path2" /> + <circle + cx="8" + cy="1044.4" + r="3" + fill="#fff" + fill-opacity=".58824" + stroke-linecap="round" + stroke-linejoin="round" + stroke-opacity=".32549" + stroke-width="3" + id="circle4" /> + </g> + <g + transform="translate(-0.06779632,-1036.4)" + id="g18"> + <circle + style="fill:#ffffff;fill-opacity:0.78430996;stroke-width:3;stroke-linejoin:round;stroke-opacity:0.39216003" + cx="8" + cy="1044.4" + r="3" + id="circle16" /> + </g> </svg> diff --git a/editor/icons/icon_oriented_path_follow.svg b/editor/icons/icon_oriented_path_follow.svg new file mode 100644 index 0000000000..bd3f585e54 --- /dev/null +++ b/editor/icons/icon_oriented_path_follow.svg @@ -0,0 +1,5 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m13 0l-3 4h1.9473c-0.1385 1.3203-0.5583 1.9074-1.084 2.2754-0.64426 0.451-1.7129 0.60547-2.9629 0.73047s-2.6814 0.22053-3.9121 1.082c-0.89278 0.62493-1.5321 1.6522-1.8184 3.0957a2 2 0 0 0 -1.1699 1.8164 2 2 0 0 0 2 2 2 2 0 0 0 2 -2 2 2 0 0 0 -0.84961 -1.6328c0.19235-0.88496 0.55306-1.3373 0.98633-1.6406 0.64426-0.451 1.7129-0.60547 2.9629-0.73047s2.6814-0.22053 3.9121-1.082c1.0528-0.73697 1.7552-2.032 1.9375-3.9141h2.0508l-3-4z" fill="#fc9c9c" fill-opacity=".99608"/> +</g> +</svg> diff --git a/editor/icons/icon_play_overlay.svg b/editor/icons/icon_play_overlay.svg new file mode 100644 index 0000000000..eff33f1b6b --- /dev/null +++ b/editor/icons/icon_play_overlay.svg @@ -0,0 +1,4 @@ +<svg width="64" height="64" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"> + <rect x="0" y="0" width="64" height="64" rx="5" ry="5" fill="#044B94" fill-opacity="0.6"/> + <path d="M16 16 L48 32 L16 48" fill="#f2f2f2"/> +</svg>
\ No newline at end of file diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index 1d7545f182..2fb3bf7b1e 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -339,7 +339,7 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) { NodeMap nm; nm.node = node; node_map[p_node->id] = nm; - node_name_map[p_node->name] = p_node->id; + node_name_map[node->get_name()] = p_node->id; Transform xf = p_node->default_transform; xf = collada.fix_transform(xf) * p_node->post_transform; diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index af79f9946a..f4be6e8d59 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -1163,7 +1163,7 @@ Error EditorSceneImporterGLTF::_parse_images(GLTFState &state, const String &p_b continue; } - if (mimetype.findn("jpg") != -1) { + if (mimetype.findn("jpeg") != -1) { //is a jpg Ref<Image> img = Image::_jpg_mem_loader_func(data_ptr, data_size); diff --git a/editor/import/resource_importer_bitmask.cpp b/editor/import/resource_importer_bitmask.cpp index a5d3959952..7b330936f6 100644 --- a/editor/import/resource_importer_bitmask.cpp +++ b/editor/import/resource_importer_bitmask.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* resource_importer_bitmask.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "resource_importer_bitmask.h" #include "core/image.h" #include "editor/editor_file_system.h" diff --git a/editor/import/resource_importer_bitmask.h b/editor/import/resource_importer_bitmask.h index 8a3cafa7ce..f3537df819 100644 --- a/editor/import/resource_importer_bitmask.h +++ b/editor/import/resource_importer_bitmask.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* resource_importer_bitmask.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 RESOURCE_IMPORTER_BITMASK_H #define RESOURCE_IMPORTER_BITMASK_H diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 128196be5a..f91802b352 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -407,6 +407,7 @@ ImportDock::ImportDock() { set_name("Import"); imported = memnew(Label); imported->add_style_override("normal", EditorNode::get_singleton()->get_gui_base()->get_stylebox("normal", "LineEdit")); + imported->set_clip_text(true); add_child(imported); HBoxContainer *hb = memnew(HBoxContainer); add_margin_child(TTR("Import As:"), hb); diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp new file mode 100644 index 0000000000..4159a3658e --- /dev/null +++ b/editor/inspector_dock.cpp @@ -0,0 +1,564 @@ +/*************************************************************************/ +/* inspector_dock.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "inspector_dock.h" + +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "editor/plugins/animation_player_editor_plugin.h" + +void InspectorDock::_menu_option(int p_option) { + switch (p_option) { + case RESOURCE_SAVE: { + _save_resource(false); + } break; + case RESOURCE_SAVE_AS: { + _save_resource(true); + } break; + + case OBJECT_REQUEST_HELP: { + if (current) { + editor->set_visible_editor(EditorNode::EDITOR_SCRIPT); + emit_signal("request_help", current->get_class()); + } + } break; + + case OBJECT_COPY_PARAMS: { + editor_data->apply_changes_in_editors(); + if (current) + editor_data->copy_object_params(current); + } break; + + case OBJECT_PASTE_PARAMS: { + editor_data->apply_changes_in_editors(); + if (current) + editor_data->paste_object_params(current); + editor_data->get_undo_redo().clear_history(); + } break; + + case OBJECT_UNIQUE_RESOURCES: { + editor_data->apply_changes_in_editors(); + if (current) { + List<PropertyInfo> props; + current->get_property_list(&props); + Map<RES, RES> duplicates; + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + continue; + + Variant v = current->get(E->get().name); + if (v.is_ref()) { + REF ref = v; + if (ref.is_valid()) { + + RES res = ref; + if (res.is_valid()) { + + if (!duplicates.has(res)) { + duplicates[res] = res->duplicate(); + } + res = duplicates[res]; + + current->set(E->get().name, res); + } + } + } + } + } + + editor_data->get_undo_redo().clear_history(); + + editor->get_editor_plugins_over()->edit(NULL); + editor->get_editor_plugins_over()->edit(current); + + } break; + + default: { + if (p_option >= OBJECT_METHOD_BASE) { + ERR_FAIL_COND(!current); + + int idx = p_option - OBJECT_METHOD_BASE; + + List<MethodInfo> methods; + current->get_method_list(&methods); + + ERR_FAIL_INDEX(idx, methods.size()); + String name = methods[idx].name; + + if (current) + current->call(name); + } + } + } +} + +void InspectorDock::_new_resource() { + new_resource_dialog->popup_create(true); +} + +void InspectorDock::_load_resource(const String &p_type) { + load_resource_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); + + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type(p_type, &extensions); + + load_resource_dialog->clear_filters(); + for (int i = 0; i < extensions.size(); i++) { + load_resource_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); + } + + load_resource_dialog->popup_centered_ratio(); +} + +void InspectorDock::_resource_file_selected(String p_file) { + RES res = ResourceLoader::load(p_file); + if (res.is_null()) { + warning_dialog->get_ok()->set_text("Ugh"); + warning_dialog->set_text(TTR("Failed to load resource.")); + return; + }; + + editor->push_item(res.operator->()); +} + +void InspectorDock::_save_resource(bool save_as) const { + uint32_t current = EditorNode::get_singleton()->get_editor_history()->get_current(); + Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL; + + ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj)) + + RES current_res = RES(Object::cast_to<Resource>(current_obj)); + + if (save_as) + editor->save_resource_as(current_res); + else + editor->save_resource(current_res); +} + +void InspectorDock::_unref_resource() const { + uint32_t current = EditorNode::get_singleton()->get_editor_history()->get_current(); + Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL; + + ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj)) + + RES current_res = RES(Object::cast_to<Resource>(current_obj)); + current_res->set_path(""); + editor->edit_current(); +} + +void InspectorDock::_copy_resource() const { + uint32_t current = EditorNode::get_singleton()->get_editor_history()->get_current(); + Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL; + + ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj)) + + RES current_res = RES(Object::cast_to<Resource>(current_obj)); + + EditorSettings::get_singleton()->set_resource_clipboard(current_res); +} + +void InspectorDock::_paste_resource() const { + RES r = EditorSettings::get_singleton()->get_resource_clipboard(); + if (r.is_valid()) { + editor->push_item(EditorSettings::get_singleton()->get_resource_clipboard().ptr(), String()); + } +} + +void InspectorDock::_prepare_history() { + EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history(); + + int history_to = MAX(0, editor_history->get_history_len() - 25); + + history_menu->get_popup()->clear(); + + Ref<Texture> base_icon = get_icon("Object", "EditorIcons"); + Set<ObjectID> already; + for (int i = editor_history->get_history_len() - 1; i >= history_to; i--) { + + ObjectID id = editor_history->get_history_obj(i); + Object *obj = ObjectDB::get_instance(id); + if (!obj || already.has(id)) { + if (history_to > 0) { + history_to--; + } + continue; + } + + already.insert(id); + + Ref<Texture> icon = get_icon("Object", "EditorIcons"); + if (has_icon(obj->get_class(), "EditorIcons")) + icon = get_icon(obj->get_class(), "EditorIcons"); + else + icon = base_icon; + + String text; + if (Object::cast_to<Resource>(obj)) { + Resource *r = Object::cast_to<Resource>(obj); + if (r->get_path().is_resource_file()) + text = r->get_path().get_file(); + else if (r->get_name() != String()) { + text = r->get_name(); + } else { + text = r->get_class(); + } + } else if (Object::cast_to<Node>(obj)) { + text = Object::cast_to<Node>(obj)->get_name(); + } else if (obj->is_class("ScriptEditorDebuggerInspectedObject")) { + text = obj->call("get_title"); + } else { + text = obj->get_class(); + } + + if (i == editor_history->get_history_pos()) { + text = "[" + text + "]"; + } + history_menu->get_popup()->add_icon_item(icon, text, i); + } +} + +void InspectorDock::_select_history(int p_idx) const { + //push it to the top, it is not correct, but it's more useful + ObjectID id = EditorNode::get_singleton()->get_editor_history()->get_history_obj(p_idx); + Object *obj = ObjectDB::get_instance(id); + if (!obj) + return; + editor->push_item(obj); +} + +void InspectorDock::_resource_created() const { + Object *c = new_resource_dialog->instance_selected(); + + ERR_FAIL_COND(!c); + Resource *r = Object::cast_to<Resource>(c); + ERR_FAIL_COND(!r); + + REF res(r); + editor->push_item(c); +} + +void InspectorDock::_resource_selected(const RES &p_res, const String &p_property) const { + if (p_res.is_null()) + return; + + RES r = p_res; + editor->push_item(r.operator->(), p_property); +} + +void InspectorDock::_edit_forward() { + if (EditorNode::get_singleton()->get_editor_history()->next()) + editor->edit_current(); +} +void InspectorDock::_edit_back() { + EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history(); + if (editor_history->previous() || editor_history->get_path_size() == 1) + editor->edit_current(); +} + +void InspectorDock::_menu_collapseall() { + inspector->collapse_all_folding(); +} + +void InspectorDock::_menu_expandall() { + inspector->expand_all_folding(); +} + +void InspectorDock::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) { + AnimationPlayerEditor::singleton->get_key_editor()->insert_value_key(p_keyed, p_value, p_advance); +} + +void InspectorDock::_transform_keyed(Object *sp, const String &p_sub, const Transform &p_key) { + Spatial *s = Object::cast_to<Spatial>(sp); + if (!s) + return; + AnimationPlayerEditor::singleton->get_key_editor()->insert_transform_key(s, p_sub, p_key); +} + +void InspectorDock::_warning_pressed() { + warning_dialog->get_ok()->set_text(TTR("Ok")); + warning_dialog->popup_centered_minsize(); +} + +Container *InspectorDock::get_addon_area() { + return this; +} + +void InspectorDock::_bind_methods() { + ClassDB::bind_method("_menu_option", &InspectorDock::_menu_option); + + ClassDB::bind_method("update_keying", &InspectorDock::update_keying); + ClassDB::bind_method("_property_keyed", &InspectorDock::_property_keyed); + ClassDB::bind_method("_transform_keyed", &InspectorDock::_transform_keyed); + + ClassDB::bind_method("_new_resource", &InspectorDock::_new_resource); + ClassDB::bind_method("_resource_file_selected", &InspectorDock::_resource_file_selected); + ClassDB::bind_method("_open_resource_selector", &InspectorDock::_open_resource_selector); + ClassDB::bind_method("_unref_resource", &InspectorDock::_unref_resource); + ClassDB::bind_method("_paste_resource", &InspectorDock::_paste_resource); + ClassDB::bind_method("_copy_resource", &InspectorDock::_copy_resource); + + ClassDB::bind_method("_select_history", &InspectorDock::_select_history); + ClassDB::bind_method("_prepare_history", &InspectorDock::_prepare_history); + ClassDB::bind_method("_resource_created", &InspectorDock::_resource_created); + ClassDB::bind_method("_resource_selected", &InspectorDock::_resource_selected, DEFVAL("")); + ClassDB::bind_method("_menu_collapseall", &InspectorDock::_menu_collapseall); + ClassDB::bind_method("_menu_expandall", &InspectorDock::_menu_expandall); + ClassDB::bind_method("_warning_pressed", &InspectorDock::_warning_pressed); + ClassDB::bind_method("_edit_forward", &InspectorDock::_edit_forward); + ClassDB::bind_method("_edit_back", &InspectorDock::_edit_back); + + ADD_SIGNAL(MethodInfo("request_help")); +} + +void InspectorDock::edit_resource(const Ref<Resource> &p_resource) { + _resource_selected(p_resource, ""); +} + +void InspectorDock::open_resource(const String &p_type) { + _load_resource(p_type); +} + +void InspectorDock::set_warning(const String &p_message) { + warning->hide(); + if (p_message != String()) { + warning->show(); + warning_dialog->set_text(p_message); + } +} + +void InspectorDock::clear() { +} + +void InspectorDock::update(Object *p_object) { + + EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history(); + backward_button->set_disabled(editor_history->is_at_beginning()); + forward_button->set_disabled(editor_history->is_at_end()); + + history_menu->set_disabled(true); + if (editor_history->get_history_len() > 0) { + history_menu->set_disabled(false); + } + editor_path->update_path(); + + current = p_object; + + if (!p_object) { + object_menu->set_disabled(true); + warning->hide(); + search->set_editable(false); + + return; + } + + bool is_resource = p_object->is_class("Resource"); + bool is_node = p_object->is_class("Node"); + + object_menu->set_disabled(false); + search->set_editable(true); + + PopupMenu *p = object_menu->get_popup(); + + p->clear(); + p->add_shortcut(ED_SHORTCUT("property_editor/expand_all", TTR("Expand all properties")), EXPAND_ALL); + p->add_shortcut(ED_SHORTCUT("property_editor/collapse_all", TTR("Collapse all properties")), COLLAPSE_ALL); + p->add_separator(); + if (is_resource) { + p->add_item(TTR("Save"), RESOURCE_SAVE); + p->add_item(TTR("Save As..."), RESOURCE_SAVE_AS); + p->add_separator(); + } + p->add_shortcut(ED_SHORTCUT("property_editor/copy_params", TTR("Copy Params")), OBJECT_COPY_PARAMS); + p->add_shortcut(ED_SHORTCUT("property_editor/paste_params", TTR("Paste Params")), OBJECT_PASTE_PARAMS); + p->add_separator(); + p->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTR("Paste Resource")), RESOURCE_PASTE); + if (is_resource) { + p->add_shortcut(ED_SHORTCUT("property_editor/copy_resource", TTR("Copy Resource")), RESOURCE_COPY); + p->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Built-In")), RESOURCE_UNREF); + } + + if (is_resource || is_node) { + p->add_separator(); + p->add_shortcut(ED_SHORTCUT("property_editor/make_subresources_unique", TTR("Make Sub-Resources Unique")), OBJECT_UNIQUE_RESOURCES); + p->add_separator(); + p->add_icon_shortcut(get_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("property_editor/open_help", TTR("Open in Help")), OBJECT_REQUEST_HELP); + } + + List<MethodInfo> methods; + p_object->get_method_list(&methods); + + if (!methods.empty()) { + + bool found = false; + List<MethodInfo>::Element *I = methods.front(); + int i = 0; + while (I) { + + if (I->get().flags & METHOD_FLAG_EDITOR) { + if (!found) { + p->add_separator(); + found = true; + } + p->add_item(I->get().name.capitalize(), OBJECT_METHOD_BASE + i); + } + i++; + I = I->next(); + } + } +} + +void InspectorDock::update_keying() { + bool valid = false; + + if (AnimationPlayerEditor::singleton->get_key_editor()->has_keying()) { + + EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history(); + if (editor_history->get_path_size() >= 1) { + + Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0)); + if (Object::cast_to<Node>(obj)) { + + valid = true; + } + } + } + + inspector->set_keying(valid); +} + +InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { + set_name("Inspector"); + set_theme(p_editor->get_gui_base()->get_theme()); + + editor = p_editor; + editor_data = &p_editor_data; + + HBoxContainer *general_options_hb = memnew(HBoxContainer); + add_child(general_options_hb); + + resource_new_button = memnew(ToolButton); + resource_new_button->set_tooltip(TTR("Create a new resource in memory and edit it.")); + resource_new_button->set_icon(get_icon("New", "EditorIcons")); + general_options_hb->add_child(resource_new_button); + resource_new_button->connect("pressed", this, "_new_resource"); + resource_new_button->set_focus_mode(Control::FOCUS_NONE); + + resource_load_button = memnew(ToolButton); + resource_load_button->set_tooltip(TTR("Load an existing resource from disk and edit it.")); + resource_load_button->set_icon(get_icon("Load", "EditorIcons")); + general_options_hb->add_child(resource_load_button); + resource_load_button->connect("pressed", this, "_open_resource_selector"); + resource_load_button->set_focus_mode(Control::FOCUS_NONE); + + general_options_hb->add_spacer(); + + backward_button = memnew(ToolButton); + general_options_hb->add_child(backward_button); + backward_button->set_icon(get_icon("Back", "EditorIcons")); + backward_button->set_flat(true); + backward_button->set_tooltip(TTR("Go to the previous edited object in history.")); + backward_button->set_disabled(true); + backward_button->connect("pressed", this, "_edit_back"); + + forward_button = memnew(ToolButton); + general_options_hb->add_child(forward_button); + forward_button->set_icon(get_icon("Forward", "EditorIcons")); + forward_button->set_flat(true); + forward_button->set_tooltip(TTR("Go to the next edited object in history.")); + forward_button->set_disabled(true); + forward_button->connect("pressed", this, "_edit_forward"); + + history_menu = memnew(MenuButton); + history_menu->set_tooltip(TTR("History of recently edited objects.")); + history_menu->set_icon(get_icon("History", "EditorIcons")); + general_options_hb->add_child(history_menu); + history_menu->connect("about_to_show", this, "_prepare_history"); + history_menu->get_popup()->connect("id_pressed", this, "_select_history"); + + HBoxContainer *node_info_hb = memnew(HBoxContainer); + add_child(node_info_hb); + + editor_path = memnew(EditorPath(editor->get_editor_history())); + editor_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); + node_info_hb->add_child(editor_path); + + object_menu = memnew(MenuButton); + object_menu->set_icon(get_icon("Tools", "EditorIcons")); + node_info_hb->add_child(object_menu); + object_menu->set_tooltip(TTR("Object properties.")); + object_menu->get_popup()->connect("id_pressed", this, "_menu_option"); + + new_resource_dialog = memnew(CreateDialog); + editor->get_gui_base()->add_child(new_resource_dialog); + new_resource_dialog->set_base_type("Resource"); + new_resource_dialog->connect("create", this, "_resource_created"); + + search = memnew(LineEdit); + search->set_h_size_flags(Control::SIZE_EXPAND_FILL); + search->set_placeholder(TTR("Filter properties")); + search->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + add_child(search); + + warning = memnew(Button); + add_child(warning); + warning->set_text(TTR("Changes may be lost!")); + warning->set_icon(get_icon("NodeWarning", "EditorIcons")); + warning->hide(); + warning->connect("pressed", this, "_warning_pressed"); + + warning_dialog = memnew(AcceptDialog); + editor->get_gui_base()->add_child(warning_dialog); + + load_resource_dialog = memnew(EditorFileDialog); + add_child(load_resource_dialog); + load_resource_dialog->set_current_dir("res://"); + load_resource_dialog->connect("file_selected", this, "_resource_file_selected"); + + inspector = memnew(EditorInspector); + add_child(inspector); + inspector->set_autoclear(true); + inspector->set_show_categories(true); + inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL); + inspector->set_use_doc_hints(true); + inspector->set_hide_script(false); + inspector->set_enable_capitalize_paths(bool(EDITOR_DEF("interface/editor/capitalize_properties", true))); + inspector->set_use_folding(!bool(EDITOR_DEF("interface/editor/disable_inspector_folding", false))); + inspector->register_text_enter(search); + inspector->set_undo_redo(&editor_data->get_undo_redo()); + + inspector->set_use_filter(true); // TODO: check me + + inspector->connect("resource_selected", this, "_resource_selected"); + inspector->connect("property_keyed", this, "_property_keyed"); +} + +InspectorDock::~InspectorDock() { +} diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h new file mode 100644 index 0000000000..688c8beed7 --- /dev/null +++ b/editor/inspector_dock.h @@ -0,0 +1,137 @@ +/*************************************************************************/ +/* inspector_dock.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 INSPECTOR_DOCK_H +#define INSPECTOR_DOCK_H + +#include "editor/animation_editor.h" +#include "editor/connections_dialog.h" +#include "editor/create_dialog.h" +#include "editor/editor_data.h" +#include "editor/editor_inspector.h" +#include "editor/editor_path.h" +#include "scene/gui/box_container.h" +#include "scene/gui/button.h" +#include "scene/gui/control.h" +#include "scene/gui/label.h" +#include "scene/gui/popup_menu.h" +#include "scene/gui/tool_button.h" + +class EditorNode; + +class InspectorDock : public VBoxContainer { + + GDCLASS(InspectorDock, VBoxContainer); + + enum MenuOptions { + RESOURCE_NEW, + RESOURCE_LOAD, + RESOURCE_SAVE, + RESOURCE_SAVE_AS, + RESOURCE_UNREF, + RESOURCE_COPY, + RESOURCE_PASTE, + OBJECT_COPY_PARAMS, + OBJECT_PASTE_PARAMS, + OBJECT_UNIQUE_RESOURCES, + OBJECT_REQUEST_HELP, + + COLLAPSE_ALL, + EXPAND_ALL, + + OBJECT_METHOD_BASE = 500 + }; + + EditorNode *editor; + EditorData *editor_data; + + EditorInspector *inspector; + + Object *current; + + ToolButton *backward_button; + ToolButton *forward_button; + + EditorFileDialog *load_resource_dialog; + CreateDialog *new_resource_dialog; + ToolButton *resource_new_button; + ToolButton *resource_load_button; + MenuButton *resource_save_button; + MenuButton *history_menu; + LineEdit *search; + + MenuButton *object_menu; + EditorPath *editor_path; + + Button *warning; + AcceptDialog *warning_dialog; + + void _menu_option(int p_option); + + void _new_resource(); + void _load_resource(const String &p_type = ""); + void _open_resource_selector() { _load_resource(); }; // just used to call from arg-less signal + void _resource_file_selected(String p_file); + void _save_resource(bool save_as) const; + void _unref_resource() const; + void _copy_resource() const; + void _paste_resource() const; + + void _warning_pressed(); + void _resource_created() const; + void _resource_selected(const RES &p_res, const String &p_property = "") const; + void _edit_forward(); + void _edit_back(); + void _menu_collapseall(); + void _menu_expandall(); + void _select_history(int p_idx) const; + void _prepare_history(); + + void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance); + void _transform_keyed(Object *sp, const String &p_sub, const Transform &p_key); + +protected: + static void _bind_methods(); + +public: + void update_keying(); + void edit_resource(const Ref<Resource> &p_resource); + void open_resource(const String &p_type); + void clear(); + void set_warning(const String &p_message); + void update(Object *p_object); + Container *get_addon_area(); + EditorInspector *get_inspector() { return inspector; } + + InspectorDock(EditorNode *p_editor, EditorData &p_editor_data); + ~InspectorDock(); +}; + +#endif diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp index 73e78ddf2a..173be01586 100644 --- a/editor/multi_node_edit.cpp +++ b/editor/multi_node_edit.cpp @@ -80,8 +80,8 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value, ur->add_undo_property(n, name, n->get(name)); } - ur->add_do_method(EditorNode::get_singleton()->get_property_editor(), "refresh"); - ur->add_undo_method(EditorNode::get_singleton()->get_property_editor(), "refresh"); + ur->add_do_method(EditorNode::get_singleton()->get_inspector(), "refresh"); + ur->add_undo_method(EditorNode::get_singleton()->get_inspector(), "refresh"); ur->commit_action(); return true; diff --git a/editor/output_strings.cpp b/editor/output_strings.cpp index b0b72510a9..e6cadee3e3 100644 --- a/editor/output_strings.cpp +++ b/editor/output_strings.cpp @@ -87,6 +87,7 @@ void OutputStrings::_notification(int p_what) { float h_ofs = (int)h_scroll->get_value(); Point2 icon_ofs = Point2(0, (font_height - (int)icon_error->get_height()) / 2); + FontDrawer drawer(font, Color(1, 1, 1)); while (E && ofs.y < (size_height - (int)margin.y)) { String str = E->get().text; diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index b387972558..23c5e36a92 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -85,7 +85,7 @@ void AnimationPlayerEditor::_notification(int p_what) { } frame->set_value(player->get_current_animation_position()); key_editor->set_anim_pos(player->get_current_animation_position()); - EditorNode::get_singleton()->get_property_editor()->refresh(); + EditorNode::get_singleton()->get_inspector()->refresh(); } else if (last_active) { //need the last frame after it stopped @@ -1073,7 +1073,7 @@ void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos, bool p_drag) updating = false; _seek_value_changed(p_pos, !p_drag); - EditorNode::get_singleton()->get_property_editor()->refresh(); + EditorNode::get_singleton()->get_inspector()->refresh(); //seekit } diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 05833704d1..d595d4dd98 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -65,7 +65,7 @@ void EditorAssetLibraryItem::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { - icon->set_normal_texture(get_icon("GodotAssetDefault", "EditorIcons")); + icon->set_normal_texture(get_icon("DefaultProjectIcon", "EditorIcons")); category->add_color_override("font_color", Color(0.5, 0.5, 0.5)); author->add_color_override("font_color", Color(0.5, 0.5, 0.5)); } @@ -110,6 +110,7 @@ EditorAssetLibraryItem::EditorAssetLibraryItem() { add_child(hb); icon = memnew(TextureButton); + icon->set_custom_minimum_size(Size2(64, 64)); icon->set_default_cursor_shape(CURSOR_POINTING_HAND); icon->connect("pressed", this, "_asset_clicked"); @@ -170,7 +171,23 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const for (int i = 0; i < preview_images.size(); i++) { if (preview_images[i].id == p_index) { - preview_images[i].button->set_icon(p_image); + if (preview_images[i].is_video) { + Ref<Image> overlay = get_icon("PlayOverlay", "EditorIcons")->get_data(); + Ref<Image> thumbnail = p_image->get_data(); + Point2 overlay_pos = Point2((thumbnail->get_width() - overlay->get_width()) / 2, (thumbnail->get_height() - overlay->get_height()) / 2); + + thumbnail->lock(); + thumbnail->blend_rect(overlay, overlay->get_used_rect(), overlay_pos); + thumbnail->unlock(); + + Ref<ImageTexture> tex; + tex.instance(); + tex->create_from_image(thumbnail); + + preview_images[i].button->set_icon(tex); + } else { + preview_images[i].button->set_icon(p_image); + } break; } } @@ -383,7 +400,7 @@ void EditorAssetLibraryItemDownload::configure(const String &p_title, int p_asse icon->set_texture(p_preview); asset_id = p_asset_id; if (!p_preview.is_valid()) - icon->set_texture(get_icon("GodotAssetDefault", "EditorIcons")); + icon->set_texture(get_icon("DefaultProjectIcon", "EditorIcons")); host = p_download_url; sha256 = p_sha256_hash; asset_installer->connect("confirmed", this, "_close"); @@ -688,13 +705,24 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByt int len = image_data.size(); PoolByteArray::Read r = image_data.read(); - Ref<Image> image = Ref<Image>(memnew(Image(r.ptr(), len))); + Ref<Image> image = Ref<Image>(memnew(Image)); + + uint8_t png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; + uint8_t jpg_signature[3] = { 255, 216, 255 }; + + if (r.ptr()) { + if (memcmp(&r[0], &png_signature[0], 8) == 0) { + image->copy_internals_from(Image::_png_mem_loader_func(r.ptr(), len)); + } else if (memcmp(&r[0], &jpg_signature[0], 3) == 0) { + image->copy_internals_from(Image::_jpg_mem_loader_func(r.ptr(), len)); + } + } if (!image->empty()) { switch (image_queue[p_queue_id].image_type) { case IMAGE_QUEUE_ICON: - image->resize(80 * EDSCALE, 80 * EDSCALE, Image::INTERPOLATE_CUBIC); + image->resize(64 * EDSCALE, 64 * EDSCALE, Image::INTERPOLATE_CUBIC); break; case IMAGE_QUEUE_THUMBNAIL: { @@ -724,7 +752,7 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByt } if (!image_set && final) { - obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("ErrorSign", "EditorIcons")); + obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("DefaultProjectIcon", "EditorIcons")); } } } @@ -733,7 +761,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons ERR_FAIL_COND(!image_queue.has(p_queue_id)); - if (p_status == HTTPRequest::RESULT_SUCCESS) { + if (p_status == HTTPRequest::RESULT_SUCCESS && p_code < HTTPClient::RESPONSE_BAD_REQUEST) { if (p_code != HTTPClient::RESPONSE_NOT_MODIFIED) { for (int i = 0; i < headers.size(); i++) { @@ -764,10 +792,10 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons _image_update(p_code == HTTPClient::RESPONSE_NOT_MODIFIED, true, p_data, p_queue_id); } else { - WARN_PRINTS("Error getting PNG file from URL: " + image_queue[p_queue_id].image_url); + // WARN_PRINTS("Error getting image file from URL: " + image_queue[p_queue_id].image_url); Object *obj = ObjectDB::get_instance(image_queue[p_queue_id].target); if (obj) { - obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("ErrorSign", "EditorIcons")); + obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("DefaultProjectIcon", "EditorIcons")); } } @@ -919,41 +947,43 @@ HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int if (to > p_page_count) to = p_page_count; - Color gray = Color(0.65, 0.65, 0.65); - hbc->add_spacer(); - hbc->add_constant_override("separation", 10); + hbc->add_constant_override("separation", 5); + Button *first = memnew(Button); + first->set_text(TTR("First")); if (p_page != 0) { - LinkButton *first = memnew(LinkButton); - first->set_text(TTR("first")); - first->add_color_override("font_color", gray); - first->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); first->connect("pressed", this, "_search", varray(0)); - hbc->add_child(first); + } else { + first->set_disabled(true); + first->set_focus_mode(Control::FOCUS_NONE); } + hbc->add_child(first); + Button *prev = memnew(Button); + prev->set_text(TTR("Previous")); if (p_page > 0) { - LinkButton *prev = memnew(LinkButton); - prev->set_text(TTR("prev")); - prev->add_color_override("font_color", gray); - prev->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); prev->connect("pressed", this, "_search", varray(p_page - 1)); - hbc->add_child(prev); + } else { + prev->set_disabled(true); + prev->set_focus_mode(Control::FOCUS_NONE); } + hbc->add_child(prev); + hbc->add_child(memnew(VSeparator)); for (int i = from; i < to; i++) { if (i == p_page) { - Label *current = memnew(Label); + Button *current = memnew(Button); current->set_text(itos(i + 1)); + current->set_disabled(true); + current->set_focus_mode(Control::FOCUS_NONE); + hbc->add_child(current); } else { - LinkButton *current = memnew(LinkButton); - current->add_color_override("font_color", gray); - current->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); + Button *current = memnew(Button); current->set_text(itos(i + 1)); current->connect("pressed", this, "_search", varray(i)); @@ -961,28 +991,26 @@ HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int } } + Button *next = memnew(Button); + next->set_text(TTR("Next")); if (p_page < p_page_count - 1) { - LinkButton *next = memnew(LinkButton); - next->set_text(TTR("next")); - next->add_color_override("font_color", gray); - next->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); next->connect("pressed", this, "_search", varray(p_page + 1)); - - hbc->add_child(next); + } else { + next->set_disabled(true); + next->set_focus_mode(Control::FOCUS_NONE); } + hbc->add_child(memnew(VSeparator)); + hbc->add_child(next); + Button *last = memnew(Button); + last->set_text(TTR("Last")); if (p_page != p_page_count - 1) { - LinkButton *last = memnew(LinkButton); - last->set_text(TTR("last")); - last->add_color_override("font_color", gray); - last->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); - hbc->add_child(last); last->connect("pressed", this, "_search", varray(p_page_count - 1)); + } else { + last->set_disabled(true); + last->set_focus_mode(Control::FOCUS_NONE); } - - Label *totals = memnew(Label); - totals->set_text("( " + itos(from * p_page_len) + " - " + itos(from * p_page_len + p_current_items - 1) + " / " + itos(p_total_items) + " )"); - hbc->add_child(totals); + hbc->add_child(last); hbc->add_spacer(); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 93aeca6632..ca5aa7039d 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -520,8 +520,17 @@ void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_Sel node = node->get_parent(); } + // Check if the canvas item is already in the list (for groups or scenes) + bool duplicate = false; + for (int j = 0; j < i; j++) { + if (r_items[j].item == canvas_item) { + duplicate = true; + break; + } + } + //Remove the item if invalid - if (!canvas_item || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || (canvas_item->has_meta("_edit_lock_") && canvas_item->get_meta("_edit_lock_"))) { + if (!canvas_item || duplicate || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || (canvas_item->has_meta("_edit_lock_") && canvas_item->get_meta("_edit_lock_"))) { r_items.remove(i); i--; } else { @@ -530,6 +539,88 @@ void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_Sel } } +void CanvasItemEditor::_get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items) { + Point2 screen_pos = transform.xform(p_pos); + + for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) { + Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().from)); + Node2D *to_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().to)); + + Vector<Vector2> bone_shape; + if (!_get_bone_shape(&bone_shape, NULL, E)) + continue; + + // Check if the point is inside the Polygon2D + if (Geometry::is_point_in_polygon(screen_pos, bone_shape)) { + // Check if the item is already in the list + bool duplicate = false; + for (int i = 0; i < r_items.size(); i++) { + if (r_items[i].item == from_node) { + duplicate = true; + break; + } + } + if (duplicate) + continue; + + // Else, add it + _SelectResult res; + res.item = from_node; + res.z_index = from_node ? from_node->get_z_index() : 0; + res.has_z = from_node; + r_items.push_back(res); + } + } +} + +bool CanvasItemEditor::_get_bone_shape(Vector<Vector2> *shape, Vector<Vector2> *outline_shape, Map<BoneKey, BoneList>::Element *bone) { + int bone_width = EditorSettings::get_singleton()->get("editors/2d/bone_width"); + int bone_outline_width = EditorSettings::get_singleton()->get("editors/2d/bone_outline_size"); + + Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(bone->key().from)); + Node2D *to_node = Object::cast_to<Node2D>(ObjectDB::get_instance(bone->key().to)); + + if (!from_node->is_inside_tree()) + return false; //may have been removed + if (!from_node) + return false; + + if (!to_node && bone->get().length == 0) + return false; + + Vector2 from = transform.xform(from_node->get_global_position()); + Vector2 to; + + if (to_node) + to = transform.xform(to_node->get_global_position()); + else + to = transform.xform(from_node->get_global_transform().xform(Vector2(bone->get().length, 0))); + + Vector2 rel = to - from; + Vector2 relt = rel.tangent().normalized() * bone_width; + Vector2 reln = rel.normalized(); + Vector2 reltn = relt.normalized(); + + if (shape) { + shape->clear(); + shape->push_back(from); + shape->push_back(from + rel * 0.2 + relt); + shape->push_back(to); + shape->push_back(from + rel * 0.2 - relt); + } + + if (outline_shape) { + outline_shape->clear(); + outline_shape->push_back(from + (-reln - reltn) * bone_outline_width); + outline_shape->push_back(from + (-reln + reltn) * bone_outline_width); + outline_shape->push_back(from + rel * 0.2 + relt + reltn * bone_outline_width); + outline_shape->push_back(to + (reln + reltn) * bone_outline_width); + outline_shape->push_back(to + (reln - reltn) * bone_outline_width); + outline_shape->push_back(from + rel * 0.2 - relt - reltn * bone_outline_width); + } + return true; +} + void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { if (!p_node) return; @@ -1787,8 +1878,13 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { // Find the item to select CanvasItem *canvas_item = NULL; Vector<_SelectResult> selection; + + // Retrieve the items _get_canvas_items_at_pos(click, selection, editor_selection->get_selection().empty() ? 1 : 0); + // Retrieve the bones + _get_bones_at_pos(click, selection); + for (int i = 0; i < selection.size(); i++) { if (editor_selection->is_selected(selection[i].item)) { // Drag the node(s) if requested @@ -2579,57 +2675,25 @@ void CanvasItemEditor::_draw_bones() { RID ci = viewport->get_canvas_item(); if (skeleton_show_bones) { - int bone_width = EditorSettings::get_singleton()->get("editors/2d/bone_width"); Color bone_color1 = EditorSettings::get_singleton()->get("editors/2d/bone_color1"); Color bone_color2 = EditorSettings::get_singleton()->get("editors/2d/bone_color2"); Color bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color"); Color bone_outline_color = EditorSettings::get_singleton()->get("editors/2d/bone_outline_color"); Color bone_selected_color = EditorSettings::get_singleton()->get("editors/2d/bone_selected_color"); - int bone_outline_size = EditorSettings::get_singleton()->get("editors/2d/bone_outline_size"); for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) { - Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().from)); - Node2D *to_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().to)); - - if (!from_node->is_inside_tree()) - continue; //may have been removed - if (!from_node) + Vector<Vector2> bone_shape; + Vector<Vector2> bone_shape_outline; + if (!_get_bone_shape(&bone_shape, &bone_shape_outline, E)) continue; - if (!to_node && E->get().length == 0) + Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().from)); + if (!from_node->is_visible_in_tree()) continue; - Vector2 from = transform.xform(from_node->get_global_position()); - Vector2 to; - - if (to_node) - to = transform.xform(to_node->get_global_position()); - else - to = transform.xform(from_node->get_global_transform().xform(Vector2(E->get().length, 0))); - - Vector2 rel = to - from; - Vector2 relt = rel.tangent().normalized() * bone_width; - Vector2 reln = rel.normalized(); - Vector2 reltn = relt.normalized(); - - Vector<Vector2> bone_shape; - bone_shape.push_back(from); - bone_shape.push_back(from + rel * 0.2 + relt); - bone_shape.push_back(to); - bone_shape.push_back(from + rel * 0.2 - relt); - - Vector<Vector2> bone_shape_outline; - bone_shape_outline.push_back(from + (-reln - reltn) * bone_outline_size); - bone_shape_outline.push_back(from + (-reln + reltn) * bone_outline_size); - bone_shape_outline.push_back(from + rel * 0.2 + relt + reltn * bone_outline_size); - bone_shape_outline.push_back(to + (reln + reltn) * bone_outline_size); - bone_shape_outline.push_back(to + (reln - reltn) * bone_outline_size); - bone_shape_outline.push_back(from + rel * 0.2 - relt - reltn * bone_outline_size); - Vector<Color> colors; if (from_node->has_meta("_edit_ik_")) { - colors.push_back(bone_ik_color); colors.push_back(bone_ik_color); colors.push_back(bone_ik_color); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index a1957b892e..4d2af11303 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -351,6 +351,7 @@ class CanvasItemEditor : public VBoxContainer { void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit = 0); + void _get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items); void _find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); bool _select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append); @@ -379,6 +380,7 @@ class CanvasItemEditor : public VBoxContainer { UndoRedo *undo_redo; bool _build_bones_list(Node *p_node); + bool _get_bone_shape(Vector<Vector2> *shape, Vector<Vector2> *outline_shape, Map<BoneKey, BoneList>::Element *bone); List<CanvasItem *> _get_edited_canvas_items(bool retreive_locked = false, bool remove_canvas_item_if_parent_in_selection = true); Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list); diff --git a/editor/plugins/collision_polygon_2d_editor_plugin.cpp b/editor/plugins/collision_polygon_2d_editor_plugin.cpp index b624d69810..672337ba2f 100644 --- a/editor/plugins/collision_polygon_2d_editor_plugin.cpp +++ b/editor/plugins/collision_polygon_2d_editor_plugin.cpp @@ -42,6 +42,7 @@ void CollisionPolygon2DEditor::_set_node(Node *p_polygon) { CollisionPolygon2DEditor::CollisionPolygon2DEditor(EditorNode *p_editor) : AbstractPolygon2DEditor(p_editor) { + node = NULL; } CollisionPolygon2DEditorPlugin::CollisionPolygon2DEditorPlugin(EditorNode *p_node) : diff --git a/editor/plugins/cube_grid_theme_editor_plugin.cpp b/editor/plugins/cube_grid_theme_editor_plugin.cpp index 81f45b9f55..68d5ea5247 100644 --- a/editor/plugins/cube_grid_theme_editor_plugin.cpp +++ b/editor/plugins/cube_grid_theme_editor_plugin.cpp @@ -198,7 +198,7 @@ void MeshLibraryEditor::_menu_cbk(int p_option) { } break; case MENU_OPTION_REMOVE_ITEM: { - String p = editor->get_property_editor()->get_selected_path(); + String p = editor->get_inspector()->get_selected_path(); if (p.begins_with("/MeshLibrary/item") && p.get_slice_count("/") >= 3) { to_erase = p.get_slice("/", 3).to_int(); diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 7c49408c35..49c54ad67d 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -91,7 +91,7 @@ void CurveEditor::set_curve(Ref<Curve> curve) { } Size2 CurveEditor::get_minimum_size() const { - return Vector2(64, 64); + return Vector2(64, 150) * EDSCALE; } void CurveEditor::_notification(int p_what) { @@ -639,7 +639,7 @@ void CurveEditor::_draw() { Ref<Font> font = get_font("font", "Label"); float font_height = font->get_height(); - const Color text_color = get_color("font_color", "Editor"); + Color text_color = get_color("font_color", "Editor"); { // X axis @@ -720,6 +720,7 @@ void CurveEditor::_draw() { // Help text if (_selected_point > 0 && _selected_point + 1 < curve.get_point_count()) { + text_color.a *= 0.4; draw_string(font, Vector2(50, font_height), TTR("Hold Shift to edit tangents individually"), text_color); } } @@ -750,87 +751,28 @@ void CurveEditor::_bind_methods() { //--------------- -CurveEditorPlugin::CurveEditorPlugin(EditorNode *p_node) { - _editor_node = p_node; - - _view = memnew(CurveEditor); - _view->set_custom_minimum_size(Size2(100, 128 * EDSCALE)); - _view->hide(); - - _toggle_button = _editor_node->add_bottom_panel_item(get_name(), _view); - _toggle_button->hide(); - - get_editor_interface()->get_resource_previewer()->add_preview_generator(memnew(CurvePreviewGenerator)); -} - -CurveEditorPlugin::~CurveEditorPlugin() { -} - -void CurveEditorPlugin::edit(Object *p_object) { - - Ref<Curve> curve_ref; - - if (_current_ref.is_valid()) { - CurveTexture *ct = Object::cast_to<CurveTexture>(*_current_ref); - if (ct) - ct->disconnect(CoreStringNames::get_singleton()->changed, this, "_curve_texture_changed"); - } - - if (p_object) { - Resource *res = Object::cast_to<Resource>(p_object); - ERR_FAIL_COND(res == NULL); - ERR_FAIL_COND(!handles(p_object)); - - _current_ref = Ref<Resource>(Object::cast_to<Resource>(p_object)); - - if (_current_ref.is_valid()) { - Curve *curve = Object::cast_to<Curve>(*_current_ref); - if (curve) - curve_ref = Ref<Curve>(curve); - else { - CurveTexture *ct = Object::cast_to<CurveTexture>(*_current_ref); - if (ct) { - ct->connect(CoreStringNames::get_singleton()->changed, this, "_curve_texture_changed"); - curve_ref = ct->get_curve(); - } - } - } +bool EditorInspectorPluginCurve::can_handle(Object *p_object) { - } else { - _current_ref = Ref<Resource>(); - } - - _view->set_curve(curve_ref); + return Object::cast_to<Curve>(p_object) != NULL; } -bool CurveEditorPlugin::handles(Object *p_object) const { - // Both handled so that we can keep the curve editor open - return Object::cast_to<Curve>(p_object) || Object::cast_to<CurveTexture>(p_object); -} +void EditorInspectorPluginCurve::parse_begin(Object *p_object) { -void CurveEditorPlugin::make_visible(bool p_visible) { - if (p_visible) { - _toggle_button->show(); - _editor_node->make_bottom_panel_item_visible(_view); - } else { - _toggle_button->hide(); - if (_view->is_visible_in_tree()) - _editor_node->hide_bottom_panel(); - } -} + Curve *curve = Object::cast_to<Curve>(p_object); + ERR_FAIL_COND(!curve); + Ref<Curve> c(curve); -void CurveEditorPlugin::_curve_texture_changed() { - // If the curve is shown indirectly as a CurveTexture is edited, - // we need to monitor when the curve property gets assigned - CurveTexture *ct = Object::cast_to<CurveTexture>(*_current_ref); - if (ct) { - _view->set_curve(ct->get_curve()); - } + CurveEditor *editor = memnew(CurveEditor); + editor->set_curve(curve); + add_custom_control(editor); } -void CurveEditorPlugin::_bind_methods() { +CurveEditorPlugin::CurveEditorPlugin(EditorNode *p_node) { + Ref<EditorInspectorPluginCurve> curve_plugin; + curve_plugin.instance(); + EditorInspector::add_inspector_plugin(curve_plugin); - ClassDB::bind_method(D_METHOD("_curve_texture_changed"), &CurveEditorPlugin::_curve_texture_changed); + get_editor_interface()->get_resource_previewer()->add_preview_generator(memnew(CurvePreviewGenerator)); } //----------------------------------- @@ -852,13 +794,13 @@ Ref<Texture> CurvePreviewGenerator::generate(const Ref<Resource> &p_from) { img_ref.instance(); Image &im = **img_ref; - im.create(thumbnail_size, thumbnail_size, 0, Image::FORMAT_RGBA8); + im.create(thumbnail_size, thumbnail_size / 2, 0, Image::FORMAT_RGBA8); im.lock(); Color bg_color(0.1, 0.1, 0.1, 1.0); for (int i = 0; i < thumbnail_size; i++) { - for (int j = 0; j < thumbnail_size; j++) { + for (int j = 0; j < thumbnail_size / 2; j++) { im.set_pixel(i, j, bg_color); } } diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h index 97f1ba2fa1..255f359ed2 100644 --- a/editor/plugins/curve_editor_plugin.h +++ b/editor/plugins/curve_editor_plugin.h @@ -119,28 +119,19 @@ private: float _tangents_length; }; +class EditorInspectorPluginCurve : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginCurve, EditorInspectorPlugin) +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); +}; + class CurveEditorPlugin : public EditorPlugin { GDCLASS(CurveEditorPlugin, EditorPlugin) public: CurveEditorPlugin(EditorNode *p_node); - ~CurveEditorPlugin(); String get_name() const { return "Curve"; } - bool has_main_screen() const { return false; } - void edit(Object *p_object); - bool handles(Object *p_object) const; - void make_visible(bool p_visible); - -private: - static void _bind_methods(); - - void _curve_texture_changed(); - -private: - CurveEditor *_view; - Ref<Resource> _current_ref; - EditorNode *_editor_node; - ToolButton *_toggle_button; }; class CurvePreviewGenerator : public EditorResourcePreviewGenerator { diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 8542296bde..ac4166bb98 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -37,6 +37,7 @@ #include "io/resource_loader.h" #include "os/os.h" #include "scene/resources/bit_mask.h" +#include "scene/resources/dynamic_font.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" @@ -467,15 +468,6 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) { Color text_color = EditorSettings::get_singleton()->get("text_editor/highlighting/text_color"); Color symbol_color = EditorSettings::get_singleton()->get("text_editor/highlighting/symbol_color"); - if (EditorSettings::get_singleton()->get("text_editor/theme/color_theme") == "Adaptive") { - Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme(); - - bg_color = tm->get_color("text_editor/highlighting/background_color", "Editor"); - keyword_color = tm->get_color("text_editor/highlighting/keyword_color", "Editor"); - text_color = tm->get_color("text_editor/highlighting/text_color", "Editor"); - symbol_color = tm->get_color("text_editor/highlighting/symbol_color", "Editor"); - } - img->lock(); if (bg_color.a == 0) @@ -942,3 +934,100 @@ EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() { VS::get_singleton()->free(camera); VS::get_singleton()->free(scenario); } + +/////////////////////////////////////////////////////////////////////////// + +void EditorFontPreviewPlugin::_preview_done(const Variant &p_udata) { + + preview_done = true; +} + +void EditorFontPreviewPlugin::_bind_methods() { + + ClassDB::bind_method("_preview_done", &EditorFontPreviewPlugin::_preview_done); +} + +bool EditorFontPreviewPlugin::handles(const String &p_type) const { + + return ClassDB::is_parent_class(p_type, "DynamicFontData"); +} + +Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) { + if (canvas.is_valid()) { + VS::get_singleton()->viewport_remove_canvas(viewport, canvas); + } + + canvas = VS::get_singleton()->canvas_create(); + canvas_item = VS::get_singleton()->canvas_item_create(); + + VS::get_singleton()->viewport_attach_canvas(viewport, canvas); + VS::get_singleton()->canvas_item_set_parent(canvas_item, canvas); + + Ref<DynamicFontData> SampledFont; + SampledFont.instance(); + SampledFont->set_font_path(p_path); + + int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); + thumbnail_size *= EDSCALE; + + Ref<DynamicFont> sampled_font; + sampled_font.instance(); + sampled_font->set_size(50); + sampled_font->set_font_data(SampledFont); + + String sampled_text = "Abg"; + Vector2 size = sampled_font->get_string_size(sampled_text); + + Vector2 pos; + + pos.x = 64 - size.x / 2; + pos.y = 80; + + Ref<Font> font = sampled_font; + + font->draw(canvas_item, pos, sampled_text); + + VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture + + preview_done = false; + VS::get_singleton()->request_frame_drawn_callback(this, "_preview_done", Variant()); + + while (!preview_done) { + OS::get_singleton()->delay_usec(10); + } + + Ref<Image> img = VS::get_singleton()->VS::get_singleton()->texture_get_data(viewport_texture); + ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>()); + + img->convert(Image::FORMAT_RGBA8); + img->resize(thumbnail_size, thumbnail_size); + + post_process_preview(img); + + Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); + ptex->create_from_image(img, 0); + + return ptex; +} + +Ref<Texture> EditorFontPreviewPlugin::generate(const RES &p_from) { + + return generate_from_path(p_from->get_path()); +} + +EditorFontPreviewPlugin::EditorFontPreviewPlugin() { + + viewport = VS::get_singleton()->viewport_create(); + VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_DISABLED); + VS::get_singleton()->viewport_set_vflip(viewport, true); + VS::get_singleton()->viewport_set_size(viewport, 128, 128); + VS::get_singleton()->viewport_set_active(viewport, true); + viewport_texture = VS::get_singleton()->viewport_get_texture(viewport); +} + +EditorFontPreviewPlugin::~EditorFontPreviewPlugin() { + + VS::get_singleton()->free(canvas_item); + VS::get_singleton()->free(canvas); + VS::get_singleton()->free(viewport); +} diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index 35b5c3a5f0..332f991b49 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -140,4 +140,27 @@ public: ~EditorMeshPreviewPlugin(); }; +class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator { + + GDCLASS(EditorFontPreviewPlugin, EditorResourcePreviewGenerator) + + RID viewport; + RID viewport_texture; + RID canvas; + RID canvas_item; + volatile bool preview_done; + + void _preview_done(const Variant &p_udata); + +protected: + static void _bind_methods(); + +public: + virtual bool handles(const String &p_type) const; + virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate_from_path(const String &p_path); + + EditorFontPreviewPlugin(); + ~EditorFontPreviewPlugin(); +}; #endif // EDITORPREVIEWPLUGINS_H diff --git a/editor/plugins/gradient_editor_plugin.cpp b/editor/plugins/gradient_editor_plugin.cpp index e89cb68935..442bd52ea7 100644 --- a/editor/plugins/gradient_editor_plugin.cpp +++ b/editor/plugins/gradient_editor_plugin.cpp @@ -33,77 +33,70 @@ #include "canvas_item_editor_plugin.h" #include "spatial_editor_plugin.h" -GradientEditorPlugin::GradientEditorPlugin(EditorNode *p_node) { - - editor = p_node; - ramp_editor = memnew(GradientEdit); - - add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM, ramp_editor); - - ramp_editor->set_custom_minimum_size(Size2(100, 48)); - ramp_editor->hide(); - ramp_editor->connect("ramp_changed", this, "ramp_changed"); +Size2 GradientEditor::get_minimum_size() const { + return Size2(0, 60) * EDSCALE; } +void GradientEditor::_gradient_changed() { -void GradientEditorPlugin::edit(Object *p_object) { - - Gradient *gradient = Object::cast_to<Gradient>(p_object); - if (!gradient) + if (editing) return; - gradient_ref = Ref<Gradient>(gradient); - ramp_editor->set_points(gradient_ref->get_points()); -} -bool GradientEditorPlugin::handles(Object *p_object) const { + editing = true; + Vector<Gradient::Point> points = gradient->get_points(); + set_points(points); + editing = false; +} - return p_object->is_class("Gradient"); +void GradientEditor::_ramp_changed() { + + editing = true; + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo->create_action("Gradient Edited"); + undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets()); + undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors()); + undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets()); + undo_redo->add_undo_method(gradient.ptr(), "set_colors", gradient->get_colors()); + undo_redo->commit_action(); + editing = false; } -void GradientEditorPlugin::make_visible(bool p_visible) { +void GradientEditor::_bind_methods() { - if (p_visible) { - ramp_editor->show(); - } else { - ramp_editor->hide(); - } + ClassDB::bind_method("_gradient_changed", &GradientEditor::_gradient_changed); + ClassDB::bind_method("_ramp_changed", &GradientEditor::_ramp_changed); } -void GradientEditorPlugin::_ramp_changed() { - - if (gradient_ref.is_valid()) { +void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) { + gradient = p_gradient; + connect("ramp_changed", this, "_ramp_changed"); + gradient->connect("changed", this, "_gradient_changed"); + set_points(gradient->get_points()); +} - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); +GradientEditor::GradientEditor() { + editing = false; +} - //Not sure if I should convert this data to PoolVector - Vector<float> new_offsets = ramp_editor->get_offsets(); - Vector<Color> new_colors = ramp_editor->get_colors(); - Vector<float> old_offsets = gradient_ref->get_offsets(); - Vector<Color> old_colors = gradient_ref->get_colors(); +/////////////////////// - if (old_offsets.size() != new_offsets.size()) - ur->create_action(TTR("Add/Remove Color Ramp Point")); - else - ur->create_action(TTR("Modify Color Ramp"), UndoRedo::MERGE_ENDS); - ur->add_do_method(this, "undo_redo_gradient", new_offsets, new_colors); - ur->add_undo_method(this, "undo_redo_gradient", old_offsets, old_colors); - ur->commit_action(); +bool EditorInspectorPluginGradient::can_handle(Object *p_object) { - //color_ramp_ref->set_points(ramp_editor->get_points()); - } + return Object::cast_to<Gradient>(p_object) != NULL; } -void GradientEditorPlugin::_undo_redo_gradient(const Vector<float> &offsets, const Vector<Color> &colors) { +void EditorInspectorPluginGradient::parse_begin(Object *p_object) { - gradient_ref->set_offsets(offsets); - gradient_ref->set_colors(colors); - ramp_editor->set_points(gradient_ref->get_points()); - ramp_editor->update(); -} + Gradient *gradient = Object::cast_to<Gradient>(p_object); + Ref<Gradient> g(gradient); -GradientEditorPlugin::~GradientEditorPlugin() { + GradientEditor *editor = memnew(GradientEditor); + editor->set_gradient(g); + add_custom_control(editor); } -void GradientEditorPlugin::_bind_methods() { - ClassDB::bind_method(D_METHOD("ramp_changed"), &GradientEditorPlugin::_ramp_changed); - ClassDB::bind_method(D_METHOD("undo_redo_gradient", "offsets", "colors"), &GradientEditorPlugin::_undo_redo_gradient); +GradientEditorPlugin::GradientEditorPlugin(EditorNode *p_node) { + + Ref<EditorInspectorPluginGradient> plugin; + plugin.instance(); + add_inspector_plugin(plugin); } diff --git a/editor/plugins/gradient_editor_plugin.h b/editor/plugins/gradient_editor_plugin.h index 52f4c59575..0c878b168f 100644 --- a/editor/plugins/gradient_editor_plugin.h +++ b/editor/plugins/gradient_editor_plugin.h @@ -35,28 +35,39 @@ #include "editor/editor_plugin.h" #include "scene/gui/gradient_edit.h" -class GradientEditorPlugin : public EditorPlugin { +class GradientEditor : public GradientEdit { + GDCLASS(GradientEditor, GradientEdit) - GDCLASS(GradientEditorPlugin, EditorPlugin); + bool editing; + Ref<Gradient> gradient; - Ref<Gradient> gradient_ref; - GradientEdit *ramp_editor; - EditorNode *editor; + void _gradient_changed(); + void _ramp_changed(); protected: static void _bind_methods(); - void _ramp_changed(); - void _undo_redo_gradient(const Vector<float> &offsets, const Vector<Color> &colors); + +public: + virtual Size2 get_minimum_size() const; + void set_gradient(const Ref<Gradient> &p_gradient); + GradientEditor(); +}; + +class EditorInspectorPluginGradient : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginGradient, EditorInspectorPlugin) +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); +}; + +class GradientEditorPlugin : public EditorPlugin { + + GDCLASS(GradientEditorPlugin, EditorPlugin); public: virtual String get_name() const { return "ColorRamp"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); GradientEditorPlugin(EditorNode *p_node); - ~GradientEditorPlugin(); }; #endif /* TOOLS_EDITOR_PLUGINS_COLOR_RAMP_EDITOR_PLUGIN_H_ */ diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp index 8b44f672b0..f75fb0d109 100644 --- a/editor/plugins/item_list_editor_plugin.cpp +++ b/editor/plugins/item_list_editor_plugin.cpp @@ -388,7 +388,7 @@ ItemListEditor::ItemListEditor() { vbc->add_child(property_editor); property_editor->set_v_size_flags(SIZE_EXPAND_FILL); - tree = property_editor->get_scene_tree(); + tree = property_editor->get_property_tree(); } ItemListEditor::~ItemListEditor() { diff --git a/editor/plugins/line_2d_editor_plugin.cpp b/editor/plugins/line_2d_editor_plugin.cpp index 47d5a73078..ba6452c1d1 100644 --- a/editor/plugins/line_2d_editor_plugin.cpp +++ b/editor/plugins/line_2d_editor_plugin.cpp @@ -64,6 +64,7 @@ void Line2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, con Line2DEditor::Line2DEditor(EditorNode *p_editor) : AbstractPolygon2DEditor(p_editor) { + node = NULL; } Line2DEditorPlugin::Line2DEditorPlugin(EditorNode *p_node) : diff --git a/editor/plugins/navigation_polygon_editor_plugin.cpp b/editor/plugins/navigation_polygon_editor_plugin.cpp index d1edf1ae10..0332e15b0e 100644 --- a/editor/plugins/navigation_polygon_editor_plugin.cpp +++ b/editor/plugins/navigation_polygon_editor_plugin.cpp @@ -123,6 +123,7 @@ void NavigationPolygonEditor::_create_resource() { NavigationPolygonEditor::NavigationPolygonEditor(EditorNode *p_editor) : AbstractPolygon2DEditor(p_editor) { + node = NULL; } NavigationPolygonEditorPlugin::NavigationPolygonEditorPlugin(EditorNode *p_node) : diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index f04e0a801c..ed41e1931e 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -1035,6 +1035,7 @@ Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const { Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : AbstractPolygon2DEditor(p_editor) { + node = NULL; snap_step = Vector2(10, 10); use_snap = false; snap_show_grid = false; diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index fa674e1e34..94dcbd8e18 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1182,12 +1182,13 @@ void ScriptEditor::_notification(int p_what) { script_forward->set_icon(get_icon("Forward", "EditorIcons")); script_back->set_icon(get_icon("Back", "EditorIcons")); + members_overview_alphabeta_sort_button->set_icon(get_icon("Sort", "EditorIcons")); } break; case NOTIFICATION_READY: { get_tree()->connect("tree_changed", this, "_tree_changed"); - editor->connect("request_help", this, "_request_help"); + editor->get_inspector_dock()->connect("request_help", this, "_request_help"); editor->connect("request_help_search", this, "_help_search"); editor->connect("request_help_index", this, "_help_index"); } break; @@ -1403,17 +1404,25 @@ void ScriptEditor::_update_members_overview_visibility() { ScriptEditorBase *se = _get_current_editor(); if (!se) { + members_overview_buttons_hbox->set_visible(false); members_overview->set_visible(false); return; } if (members_overview_enabled && se->show_members_overview()) { + members_overview_buttons_hbox->set_visible(true); members_overview->set_visible(true); } else { + members_overview_buttons_hbox->set_visible(false); members_overview->set_visible(false); } } +void ScriptEditor::_toggle_members_overview_alpha_sort(bool p_alphabetic_sort) { + EditorSettings::get_singleton()->set("text_editor/tools/sort_members_outline_alphabetically", p_alphabetic_sort); + _update_members_overview(); +} + void ScriptEditor::_update_members_overview() { members_overview->clear(); @@ -1423,6 +1432,10 @@ void ScriptEditor::_update_members_overview() { } Vector<String> functions = se->get_functions(); + if (EditorSettings::get_singleton()->get("text_editor/tools/sort_members_outline_alphabetically")) { + functions.sort(); + } + for (int i = 0; i < functions.size(); i++) { members_overview->add_item(functions[i].get_slice(":", 0)); members_overview->set_item_metadata(i, functions[i].get_slice(":", 1).to_int() - 1); @@ -1445,6 +1458,7 @@ void ScriptEditor::_update_help_overview_visibility() { } if (help_overview_enabled) { + members_overview_buttons_hbox->set_visible(false); help_overview->set_visible(true); } else { help_overview->set_visible(false); @@ -1536,9 +1550,10 @@ void ScriptEditor::_update_script_names() { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); if (se) { - String name = se->get_name(); Ref<Texture> icon = se->get_icon(); String path = se->get_edited_script()->get_path(); + bool built_in = !path.is_resource_file(); + String name = built_in ? path.get_file() : se->get_name(); _ScriptEditorItemData sd; sd.icon = icon; @@ -2596,6 +2611,8 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_live_auto_reload_running_scripts", &ScriptEditor::_live_auto_reload_running_scripts); ClassDB::bind_method("_unhandled_input", &ScriptEditor::_unhandled_input); ClassDB::bind_method("_script_list_gui_input", &ScriptEditor::_script_list_gui_input); + ClassDB::bind_method("_toggle_members_overview_alpha_sort", &ScriptEditor::_toggle_members_overview_alpha_sort); + ClassDB::bind_method("_update_members_overview", &ScriptEditor::_update_members_overview); ClassDB::bind_method("_script_changed", &ScriptEditor::_script_changed); ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts); ClassDB::bind_method("_on_find_in_files_requested", &ScriptEditor::_on_find_in_files_requested); @@ -2656,14 +2673,33 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { add_child(context_menu); context_menu->connect("id_pressed", this, "_menu_option"); + members_overview_vbox = memnew(VBoxContainer); + members_overview_vbox->set_custom_minimum_size(Size2(0, 90)); + members_overview_vbox->set_v_size_flags(SIZE_EXPAND_FILL); + + list_split->add_child(members_overview_vbox); + members_overview_buttons_hbox = memnew(HBoxContainer); + members_overview_vbox->add_child(members_overview_buttons_hbox); + + members_overview_alphabeta_sort_button = memnew(ToolButton); + members_overview_alphabeta_sort_button->set_tooltip(TTR("Toggle alphabetical sorting of the method list.")); + members_overview_alphabeta_sort_button->set_toggle_mode(true); + members_overview_alphabeta_sort_button->set_pressed(EditorSettings::get_singleton()->get("text_editor/tools/sort_members_outline_alphabetically")); + members_overview_alphabeta_sort_button->connect("toggled", this, "_toggle_members_overview_alpha_sort"); + + members_overview_buttons_hbox->add_child(members_overview_alphabeta_sort_button); + members_overview = memnew(ItemList); - list_split->add_child(members_overview); + members_overview_vbox->add_child(members_overview); + members_overview->set_allow_reselect(true); members_overview->set_custom_minimum_size(Size2(0, 90)); //need to give a bit of limit to avoid it from disappearing members_overview->set_v_size_flags(SIZE_EXPAND_FILL); + members_overview->set_allow_rmb_select(true); + members_overview->set_drag_forwarding(this); help_overview = memnew(ItemList); - list_split->add_child(help_overview); + members_overview_vbox->add_child(help_overview); help_overview->set_allow_reselect(true); help_overview->set_custom_minimum_size(Size2(0, 90)); //need to give a bit of limit to avoid it from disappearing help_overview->set_v_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 9f37b18d7d..a2ff47cd99 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -199,6 +199,9 @@ class ScriptEditor : public PanelContainer { ItemList *script_list; HSplitContainer *script_split; ItemList *members_overview; + VBoxContainer *members_overview_vbox; + HBoxContainer *members_overview_buttons_hbox; + ToolButton *members_overview_alphabeta_sort_button; bool members_overview_enabled; ItemList *help_overview; bool help_overview_enabled; @@ -318,6 +321,7 @@ class ScriptEditor : public PanelContainer { void _update_members_overview_visibility(); void _update_members_overview(); + void _toggle_members_overview_alpha_sort(bool p_alphabetic_sort); void _update_script_names(); bool _sort_list_on_update; diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp index e372f792d6..08bfebefbd 100644 --- a/editor/plugins/skeleton_2d_editor_plugin.cpp +++ b/editor/plugins/skeleton_2d_editor_plugin.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* skeleton_2d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "skeleton_2d_editor_plugin.h" #include "canvas_item_editor_plugin.h" diff --git a/editor/plugins/skeleton_2d_editor_plugin.h b/editor/plugins/skeleton_2d_editor_plugin.h index bbe2a3a6f2..26ab4328b0 100644 --- a/editor/plugins/skeleton_2d_editor_plugin.h +++ b/editor/plugins/skeleton_2d_editor_plugin.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* skeleton_2d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 SKELETON_2D_EDITOR_PLUGIN_H #define SKELETON_2D_EDITOR_PLUGIN_H diff --git a/editor/plugins/skeleton_editor_plugin.cpp b/editor/plugins/skeleton_editor_plugin.cpp index c41e3b546f..40a696119e 100644 --- a/editor/plugins/skeleton_editor_plugin.cpp +++ b/editor/plugins/skeleton_editor_plugin.cpp @@ -154,10 +154,7 @@ SkeletonEditor::SkeletonEditor() { options->hide(); } -SkeletonEditor::~SkeletonEditor() { - SpatialEditor::get_singleton()->remove_child(options); - memdelete(options); -} +SkeletonEditor::~SkeletonEditor() {} void SkeletonEditorPlugin::edit(Object *p_object) { skeleton_editor->edit(Object::cast_to<Skeleton>(p_object)); @@ -183,7 +180,4 @@ SkeletonEditorPlugin::SkeletonEditorPlugin(EditorNode *p_node) { editor->get_viewport()->add_child(skeleton_editor); } -SkeletonEditorPlugin::~SkeletonEditorPlugin() { - editor->get_viewport()->remove_child(skeleton_editor); - memdelete(skeleton_editor); -} +SkeletonEditorPlugin::~SkeletonEditorPlugin() {} diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp index 49816fe2ae..66673cca00 100644 --- a/editor/plugins/sprite_editor_plugin.cpp +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* sprite_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "sprite_editor_plugin.h" #include "canvas_item_editor_plugin.h" diff --git a/editor/plugins/sprite_editor_plugin.h b/editor/plugins/sprite_editor_plugin.h index 17aa3eb1f9..238227e4a0 100644 --- a/editor/plugins/sprite_editor_plugin.h +++ b/editor/plugins/sprite_editor_plugin.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* sprite_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 SPRITE_EDITOR_PLUGIN_H #define SPRITE_EDITOR_PLUGIN_H diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp index f4faab69ed..f6d98cb4c7 100644 --- a/editor/plugins/style_box_editor_plugin.cpp +++ b/editor/plugins/style_box_editor_plugin.cpp @@ -30,7 +30,26 @@ #include "style_box_editor_plugin.h" -void StyleBoxEditor::edit(const Ref<StyleBox> &p_stylebox) { +bool EditorInspectorPluginStyleBox::can_handle(Object *p_object) { + + return Object::cast_to<StyleBox>(p_object) != NULL; +} + +void EditorInspectorPluginStyleBox::parse_begin(Object *p_object) { + + Ref<StyleBox> sb = Ref<StyleBox>(Object::cast_to<StyleBox>(p_object)); + + StyleBoxPreview *preview = memnew(StyleBoxPreview); + preview->edit(sb); + add_custom_control(preview); +} +bool EditorInspectorPluginStyleBox::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) { + return false; //do not want +} +void EditorInspectorPluginStyleBox::parse_end() { +} + +void StyleBoxPreview::edit(const Ref<StyleBox> &p_stylebox) { if (stylebox.is_valid()) stylebox->disconnect("changed", this, "_sb_changed"); @@ -39,71 +58,33 @@ void StyleBoxEditor::edit(const Ref<StyleBox> &p_stylebox) { preview->add_style_override("panel", stylebox); stylebox->connect("changed", this, "_sb_changed"); } + _sb_changed(); } -void StyleBoxEditor::_sb_changed() { +void StyleBoxPreview::_sb_changed() { preview->update(); + if (stylebox.is_valid()) { + Size2 ms = stylebox->get_minimum_size() * 4 / 3; + ms.height = MAX(ms.height, 150 * EDSCALE); + preview->set_custom_minimum_size(ms); + } } -void StyleBoxEditor::_bind_methods() { +void StyleBoxPreview::_bind_methods() { - ClassDB::bind_method("_sb_changed", &StyleBoxEditor::_sb_changed); - //ClassDB::bind_method("_import",&StyleBoxEditor::_import); - //ClassDB::bind_method("_import_accept",&StyleBoxEditor::_import_accept); - //ClassDB::bind_method("_preview_text_changed",&StyleBoxEditor::_preview_text_changed); + ClassDB::bind_method("_sb_changed", &StyleBoxPreview::_sb_changed); } -StyleBoxEditor::StyleBoxEditor() { - - panel = memnew(Panel); - add_child(panel); - panel->set_anchors_and_margins_preset(Control::PRESET_WIDE); - - Label *l = memnew(Label); - l->set_text(TTR("StyleBox Preview:")); - l->set_position(Point2(5, 5)); - panel->add_child(l); +StyleBoxPreview::StyleBoxPreview() { preview = memnew(Panel); - panel->add_child(preview); - preview->set_position(Point2(50, 50)); - preview->set_size(Size2(200, 100)); -} - -void StyleBoxEditorPlugin::edit(Object *p_node) { - - if (Object::cast_to<StyleBox>(p_node)) { - stylebox_editor->edit(Object::cast_to<StyleBox>(p_node)); - stylebox_editor->show(); - } else - stylebox_editor->hide(); -} - -bool StyleBoxEditorPlugin::handles(Object *p_node) const { - - return p_node->is_class("StyleBox"); -} - -void StyleBoxEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - button->show(); - EditorNode::get_singleton()->make_bottom_panel_item_visible(stylebox_editor); - - } else { - if (stylebox_editor->is_visible_in_tree()) - EditorNode::get_singleton()->hide_bottom_panel(); - button->hide(); - } + add_margin_child(TTR("Preview:"), preview); } StyleBoxEditorPlugin::StyleBoxEditorPlugin(EditorNode *p_node) { - stylebox_editor = memnew(StyleBoxEditor); - stylebox_editor->set_custom_minimum_size(Size2(0, 250)); - - //p_node->get_viewport()->add_child(stylebox_editor); - button = p_node->add_bottom_panel_item(TTR("StyleBox"), stylebox_editor); - button->hide(); + Ref<EditorInspectorPluginStyleBox> inspector_plugin; + inspector_plugin.instance(); + add_inspector_plugin(inspector_plugin); } diff --git a/editor/plugins/style_box_editor_plugin.h b/editor/plugins/style_box_editor_plugin.h index 34d8caaeb6..6b0d7e57a8 100644 --- a/editor/plugins/style_box_editor_plugin.h +++ b/editor/plugins/style_box_editor_plugin.h @@ -31,18 +31,17 @@ #ifndef STYLE_BOX_EDITOR_PLUGIN_H #define STYLE_BOX_EDITOR_PLUGIN_H +#include "editor/editor_inspector.h" #include "editor/editor_node.h" #include "scene/gui/option_button.h" #include "scene/gui/texture_rect.h" #include "scene/resources/style_box.h" -class StyleBoxEditor : public Control { +class StyleBoxPreview : public VBoxContainer { - GDCLASS(StyleBoxEditor, Control); + GDCLASS(StyleBoxPreview, VBoxContainer); - Panel *panel; Panel *preview; - Ref<StyleBox> stylebox; void _sb_changed(); @@ -53,23 +52,24 @@ protected: public: void edit(const Ref<StyleBox> &p_stylebox); - StyleBoxEditor(); + StyleBoxPreview(); +}; + +class EditorInspectorPluginStyleBox : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginStyleBox, EditorInspectorPlugin) +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); + virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage); + virtual void parse_end(); }; class StyleBoxEditorPlugin : public EditorPlugin { GDCLASS(StyleBoxEditorPlugin, EditorPlugin); - StyleBoxEditor *stylebox_editor; - EditorNode *editor; - Button *button; - public: virtual String get_name() const { return "StyleBox"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_node); - virtual bool handles(Object *p_node) const; - virtual void make_visible(bool p_visible); StyleBoxEditorPlugin(EditorNode *p_node); }; diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 5ba3931689..e4fdd1f251 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -601,6 +601,17 @@ void TextureRegionEditor::apply_rect(const Rect2 &rect) { void TextureRegionEditor::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_PROCESS: { + if (node_sprite) { + if (node_sprite->is_region()) { + + set_process(false); + EditorNode::get_singleton()->make_bottom_panel_item_visible(this); + } + } else { + set_process(false); + } + } break; case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_READY: { zoom_out->set_icon(get_icon("ZoomLess", "EditorIcons")); @@ -640,6 +651,23 @@ void TextureRegionEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_zoom_out"), &TextureRegionEditor::_zoom_out); } +bool TextureRegionEditor::is_stylebox() { + return obj_styleBox.is_valid(); +} + +bool TextureRegionEditor::is_atlas_texture() { + + return atlas_tex.is_valid(); +} + +bool TextureRegionEditor::is_ninepatch() { + return node_ninepatch != NULL; +} + +Sprite *TextureRegionEditor::get_sprite() { + return node_sprite; +} + void TextureRegionEditor::edit(Object *p_obj) { if (node_sprite) node_sprite->remove_change_receptor(this); @@ -670,6 +698,12 @@ void TextureRegionEditor::edit(Object *p_obj) { tile_set = Ref<TileSet>(NULL); } edit_draw->update(); + if (node_sprite && !node_sprite->is_region()) { + set_process(true); + } + if (!p_obj) { + set_process(false); + } } void TextureRegionEditor::_changed_callback(Object *p_changed, const char *p_prop) { @@ -932,8 +966,12 @@ bool TextureRegionEditorPlugin::handles(Object *p_object) const { void TextureRegionEditorPlugin::make_visible(bool p_visible) { if (p_visible) { texture_region_button->show(); - if (texture_region_button->is_pressed()) - region_editor->show(); + if (region_editor->is_stylebox() || region_editor->is_atlas_texture() || region_editor->is_ninepatch() || (region_editor->get_sprite() && region_editor->get_sprite()->is_region())) { + editor->make_bottom_panel_item_visible(region_editor); + } else { + if (texture_region_button->is_pressed()) + region_editor->show(); + } } else { texture_region_button->hide(); region_editor->edit(NULL); @@ -989,10 +1027,10 @@ TextureRegionEditorPlugin::TextureRegionEditorPlugin(EditorNode *p_node) { editor = p_node; region_editor = memnew(TextureRegionEditor(p_node)); - texture_region_button = p_node->add_bottom_panel_item(TTR("Texture Region"), region_editor); + texture_region_button = p_node->add_bottom_panel_item(TTR("TextureRegion"), region_editor); texture_region_button->set_tooltip(TTR("Texture Region Editor")); - region_editor->set_custom_minimum_size(Size2(0, 200)); + region_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE); region_editor->hide(); texture_region_button->hide(); } diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 1244953a3f..eeba1987a6 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -131,6 +131,10 @@ public: void _region_draw(); void _region_input(const Ref<InputEvent> &p_input); void _scroll_changed(float); + bool is_stylebox(); + bool is_atlas_texture(); + bool is_ninepatch(); + Sprite *get_sprite(); void edit(Object *p_obj); TextureRegionEditor(EditorNode *p_editor); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 385fa24ad8..c79cf02062 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -126,7 +126,7 @@ void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) { Transform2D shape_transform = sb->shape_owner_get_transform(E->get()); bool one_way = sb->is_shape_owner_one_way_collision_enabled(E->get()); - shape_transform.set_origin(shape_transform.get_origin() - phys_offset); + shape_transform[2] -= phys_offset - sb->get_transform().xform(shape_transform[2]); for (int k = 0; k < sb->shape_owner_get_shape_count(E->get()); k++) { @@ -667,7 +667,7 @@ void TileSetEditor::_on_workspace_draw() { if (mask & TileSet::BIND_BOTTOMRIGHT) { workspace->draw_rect(Rect2(anchor + size / 2, size / 2), c); } - } else if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) { + } else { if (mask & TileSet::BIND_TOPLEFT) { workspace->draw_rect(Rect2(anchor, size / 3), c); } @@ -821,7 +821,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { bit = TileSet::BIND_BOTTOMRIGHT; } } - } else if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) { + } else { if (pos.x < size.x / 3) { if (pos.y < size.y / 3) { bit = TileSet::BIND_TOPLEFT; @@ -884,7 +884,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { bit = TileSet::BIND_BOTTOMRIGHT; } } - } else if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) { + } else { if (pos.x < size.x / 3) { if (pos.y < size.y / 3) { bit = TileSet::BIND_TOPLEFT; @@ -1849,7 +1849,7 @@ void TileSetEditorHelper::_get_property_list(List<PropertyInfo> *p_list) const { if (selected_tile < 0 || tileset.is_null()) return; - p_list->push_back(PropertyInfo(Variant::INT, "bitmask_mode", PROPERTY_HINT_ENUM, "2x2,3x3")); + p_list->push_back(PropertyInfo(Variant::INT, "bitmask_mode", PROPERTY_HINT_ENUM, "2x2,3x3 (minimal),3x3")); p_list->push_back(PropertyInfo(Variant::VECTOR2, "layout/tile_size")); p_list->push_back(PropertyInfo(Variant::INT, "layout/spacing", PROPERTY_HINT_RANGE, "0,256,1")); } diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 666f08cb2d..0d06b71420 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -199,6 +199,7 @@ private: sp = TTR("Imported Project"); project_name->set_text(sp); + _text_changed(sp); } } @@ -222,6 +223,7 @@ private: } String sp = p.simplify_path(); project_path->set_text(sp); + _path_text_changed(sp); get_ok()->call_deferred("grab_focus"); } @@ -230,6 +232,7 @@ private: String p = p_path; String sp = p.simplify_path(); project_path->set_text(sp); + _path_text_changed(sp); get_ok()->call_deferred("grab_focus"); } @@ -263,7 +266,9 @@ private: if (d->make_dir(project_name->get_text()) == OK) { d->change_dir(project_name->get_text()); - project_path->set_text(d->get_current_dir()); + String dir_str = d->get_current_dir(); + project_path->set_text(dir_str); + _path_text_changed(dir_str); created_folder_path = d->get_current_dir(); create_dir->set_disabled(true); } else { @@ -475,7 +480,9 @@ private: _remove_created_folder(); project_path->clear(); + _path_text_changed(""); project_name->clear(); + _text_changed(""); if (status_rect->get_texture() == get_icon("StatusError", "EditorIcons")) msg->show(); @@ -540,7 +547,9 @@ public: msg->show(); get_ok()->set_disabled(true); } else if (current->has_setting("application/config/name")) { - project_name->set_text(current->get("application/config/name")); + String proj = current->get("application/config/name"); + project_name->set_text(proj); + _text_changed(proj); } project_name->call_deferred("grab_focus"); @@ -559,7 +568,9 @@ public: fdialog->set_current_dir(d->get_current_dir()); memdelete(d); } - project_name->set_text(TTR("New Game Project")); + String proj = TTR("New Game Project"); + project_name->set_text(proj); + _text_changed(proj); project_path->set_editable(true); browse->set_disabled(false); @@ -725,9 +736,11 @@ void ProjectManager::_update_project_buttons() { } } - erase_btn->set_disabled(selected_list.size() < 1); - open_btn->set_disabled(selected_list.size() < 1); - rename_btn->set_disabled(selected_list.size() < 1); + bool empty_selection = selected_list.empty(); + erase_btn->set_disabled(empty_selection); + open_btn->set_disabled(empty_selection); + rename_btn->set_disabled(empty_selection); + run_btn->set_disabled(empty_selection); } void ProjectManager::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) { diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index a4265b4e32..82fd727620 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -785,7 +785,7 @@ void ProjectSettingsEditor::popup_project_settings() { void ProjectSettingsEditor::_item_selected() { - TreeItem *ti = globals_editor->get_property_editor()->get_scene_tree()->get_selected(); + TreeItem *ti = globals_editor->get_property_editor()->get_property_tree()->get_selected(); if (!ti) return; if (!ti->get_parent()) @@ -1727,7 +1727,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { //globals_editor->hide_top_label(); globals_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); globals_editor->register_search_box(search_box); - globals_editor->get_property_editor()->get_scene_tree()->connect("cell_selected", this, "_item_selected"); + globals_editor->get_property_editor()->get_property_tree()->connect("cell_selected", this, "_item_selected"); globals_editor->get_property_editor()->connect("property_toggled", this, "_item_checked", varray(), CONNECT_DEFERRED); globals_editor->get_property_editor()->connect("property_edited", this, "_settings_prop_edited"); diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index e0063925b1..e912ebe03a 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -4212,7 +4212,7 @@ void PropertyEditor::_bind_methods() { ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property"))); } -Tree *PropertyEditor::get_scene_tree() { +Tree *PropertyEditor::get_property_tree() { return tree; } @@ -4695,7 +4695,7 @@ SectionedPropertyEditor::SectionedPropertyEditor() { editor->set_v_size_flags(SIZE_EXPAND_FILL); right_vb->add_child(editor, true); - editor->get_scene_tree()->set_column_titles_visible(false); + editor->get_property_tree()->set_column_titles_visible(false); editor->hide_top_label(); diff --git a/editor/property_editor.h b/editor/property_editor.h index 017a190adb..56743822d2 100644 --- a/editor/property_editor.h +++ b/editor/property_editor.h @@ -275,7 +275,7 @@ public: String get_selected_path() const; - Tree *get_scene_tree(); + Tree *get_property_tree(); Label *get_top_label(); void hide_top_label(); void update_tree(); @@ -309,6 +309,7 @@ public: void collapse_all_folding(); void expand_all_folding(); + PropertyEditor(); ~PropertyEditor(); }; diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp new file mode 100644 index 0000000000..d617089142 --- /dev/null +++ b/editor/rename_dialog.cpp @@ -0,0 +1,691 @@ +/*************************************************************************/ +/* rename_dialog.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "rename_dialog.h" + +#include "editor_node.h" +#include "editor_settings.h" +#include "editor_themes.h" +#include "modules/regex/regex.h" +#include "plugins/script_editor_plugin.h" +#include "print_string.h" +#include "scene/gui/control.h" +#include "scene/gui/label.h" +#include "scene/gui/tab_container.h" + +RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_undo_redo) { + + scene_tree_editor = p_scene_tree_editor; + undo_redo = p_undo_redo; + preview_node = NULL; + + set_title(TTR("Batch Rename")); + + VBoxContainer *vbc = memnew(VBoxContainer); + add_child(vbc); + + // -- Search/Replace Area + + GridContainer *grd_main = memnew(GridContainer); + grd_main->set_columns(2); + grd_main->set_v_size_flags(SIZE_EXPAND_FILL); + vbc->add_child(grd_main); + + // ---- 1st & 2nd row + + Label *lbl_search = memnew(Label); + lbl_search->set_text(TTR("Search")); + + lne_search = memnew(LineEdit); + lne_search->set_placeholder(TTR("Search")); + lne_search->set_name("lne_search"); + lne_search->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *lbl_replace = memnew(Label); + lbl_replace->set_text(TTR("Replace")); + + lne_replace = memnew(LineEdit); + lne_replace->set_placeholder(TTR("Replace")); + lne_replace->set_name("lne_replace"); + lne_replace->set_h_size_flags(SIZE_EXPAND_FILL); + + grd_main->add_child(lbl_search); + grd_main->add_child(lbl_replace); + grd_main->add_child(lne_search); + grd_main->add_child(lne_replace); + + // ---- 3rd & 4th row + + Label *lbl_prefix = memnew(Label); + lbl_prefix->set_text(TTR("Prefix")); + + lne_prefix = memnew(LineEdit); + lne_prefix->set_placeholder(TTR("Prefix")); + lne_prefix->set_name("lne_prefix"); + lne_prefix->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *lbl_suffix = memnew(Label); + lbl_suffix->set_text(TTR("Suffix")); + + lne_suffix = memnew(LineEdit); + lne_suffix->set_placeholder(TTR("Suffix")); + lne_suffix->set_name("lne_suffix"); + lne_suffix->set_h_size_flags(SIZE_EXPAND_FILL); + + grd_main->add_child(lbl_prefix); + grd_main->add_child(lbl_suffix); + grd_main->add_child(lne_prefix); + grd_main->add_child(lne_suffix); + + // -- Feature Tabs + + const int feature_min_height = 160; + + Ref<Theme> collapse_theme = create_editor_theme(); + collapse_theme->set_icon("checked", "CheckBox", collapse_theme->get_icon("GuiTreeArrowDown", "EditorIcons")); + collapse_theme->set_icon("unchecked", "CheckBox", collapse_theme->get_icon("GuiTreeArrowRight", "EditorIcons")); + + CheckBox *chk_collapse_features = memnew(CheckBox); + chk_collapse_features->set_text(TTR("Advanced options")); + chk_collapse_features->set_theme(collapse_theme); + chk_collapse_features->set_focus_mode(FOCUS_NONE); + vbc->add_child(chk_collapse_features); + + tabc_features = memnew(TabContainer); + tabc_features->set_tab_align(TabContainer::ALIGN_LEFT); + vbc->add_child(tabc_features); + + // ---- Tab Substitute + + VBoxContainer *vbc_substitute = memnew(VBoxContainer); + vbc_substitute->set_h_size_flags(SIZE_EXPAND_FILL); + vbc_substitute->set_custom_minimum_size(Size2(0, feature_min_height)); + + vbc_substitute->set_name(TTR("Substitute")); + tabc_features->add_child(vbc_substitute); + + cbut_substitute = memnew(CheckButton); + cbut_substitute->set_text(TTR("Substitute")); + vbc_substitute->add_child(cbut_substitute); + + GridContainer *grd_substitute = memnew(GridContainer); + grd_substitute->set_columns(3); + vbc_substitute->add_child(grd_substitute); + + // Name + + but_insert_name = memnew(Button); + but_insert_name->set_text("NAME"); + but_insert_name->set_tooltip(String("${NAME}\n") + TTR("Node name")); + but_insert_name->set_focus_mode(FOCUS_NONE); + but_insert_name->connect("pressed", this, "_insert_text", make_binds("${NAME}")); + but_insert_name->set_h_size_flags(SIZE_EXPAND_FILL); + grd_substitute->add_child(but_insert_name); + + // Parent + + but_insert_parent = memnew(Button); + but_insert_parent->set_text("PARENT"); + but_insert_parent->set_tooltip(String("${PARENT}\n") + TTR("Node's parent name, if available")); + but_insert_parent->set_focus_mode(FOCUS_NONE); + but_insert_parent->connect("pressed", this, "_insert_text", make_binds("${PARENT}")); + but_insert_parent->set_h_size_flags(SIZE_EXPAND_FILL); + grd_substitute->add_child(but_insert_parent); + + // Type + + but_insert_type = memnew(Button); + but_insert_type->set_text("TYPE"); + but_insert_type->set_tooltip(String("${TYPE}\n") + TTR("Node type")); + but_insert_type->set_focus_mode(FOCUS_NONE); + but_insert_type->connect("pressed", this, "_insert_text", make_binds("${TYPE}")); + but_insert_type->set_h_size_flags(SIZE_EXPAND_FILL); + grd_substitute->add_child(but_insert_type); + + // Scene + + but_insert_scene = memnew(Button); + but_insert_scene->set_text("SCENE"); + but_insert_scene->set_tooltip(String("${SCENE}\n") + TTR("Current scene name")); + but_insert_scene->set_focus_mode(FOCUS_NONE); + but_insert_scene->connect("pressed", this, "_insert_text", make_binds("${SCENE}")); + but_insert_scene->set_h_size_flags(SIZE_EXPAND_FILL); + grd_substitute->add_child(but_insert_scene); + + // Root + + but_insert_root = memnew(Button); + but_insert_root->set_text("ROOT"); + but_insert_root->set_tooltip(String("${ROOT}\n") + TTR("Root node name")); + but_insert_root->set_focus_mode(FOCUS_NONE); + but_insert_root->connect("pressed", this, "_insert_text", make_binds("${ROOT}")); + but_insert_root->set_h_size_flags(SIZE_EXPAND_FILL); + grd_substitute->add_child(but_insert_root); + + // Count + + but_insert_count = memnew(Button); + but_insert_count->set_text("COUNTER"); + but_insert_count->set_tooltip(String("${COUNTER}\n") + TTR("Sequential integer counter.\nCompare counter options.")); + but_insert_count->set_focus_mode(FOCUS_NONE); + but_insert_count->connect("pressed", this, "_insert_text", make_binds("${COUNTER}")); + but_insert_count->set_h_size_flags(SIZE_EXPAND_FILL); + grd_substitute->add_child(but_insert_count); + + chk_per_level_counter = memnew(CheckBox); + chk_per_level_counter->set_text(TTR("Per Level counter")); + chk_per_level_counter->set_tooltip(TTR("If set the counter restarts for each group of child nodes")); + vbc_substitute->add_child(chk_per_level_counter); + + HBoxContainer *hbc_count_options = memnew(HBoxContainer); + vbc_substitute->add_child(hbc_count_options); + + Label *lbl_count_start = memnew(Label); + lbl_count_start->set_text(TTR("Start")); + lbl_count_start->set_tooltip(TTR("Initial value for the counter")); + hbc_count_options->add_child(lbl_count_start); + + spn_count_start = memnew(SpinBox); + spn_count_start->set_tooltip(TTR("Initial value for the counter")); + spn_count_start->set_step(1); + spn_count_start->set_min(0); + hbc_count_options->add_child(spn_count_start); + + Label *lbl_count_step = memnew(Label); + lbl_count_step->set_text(TTR("Step")); + lbl_count_step->set_tooltip(TTR("Ammount by which counter is incremented for each node")); + hbc_count_options->add_child(lbl_count_step); + + spn_count_step = memnew(SpinBox); + spn_count_step->set_tooltip(TTR("Ammount by which counter is incremented for each node")); + spn_count_step->set_step(1); + hbc_count_options->add_child(spn_count_step); + + Label *lbl_count_padding = memnew(Label); + lbl_count_padding->set_text(TTR("Padding")); + lbl_count_padding->set_tooltip(TTR("Minium number of digits for the counter.\nMissing digits are padded with leading zeros.")); + hbc_count_options->add_child(lbl_count_padding); + + spn_count_padding = memnew(SpinBox); + spn_count_padding->set_tooltip(TTR("Minium number of digits for the counter.\nMissing digits are padded with leading zeros.")); + spn_count_padding->set_step(1); + hbc_count_options->add_child(spn_count_padding); + + // ---- Tab RegEx + + VBoxContainer *vbc_regex = memnew(VBoxContainer); + vbc_regex->set_h_size_flags(SIZE_EXPAND_FILL); + vbc_regex->set_name(TTR("Regular Expressions")); + vbc_regex->set_custom_minimum_size(Size2(0, feature_min_height)); + tabc_features->add_child(vbc_regex); + + cbut_regex = memnew(CheckButton); + cbut_regex->set_text(TTR("Regular Expressions")); + vbc_regex->add_child(cbut_regex); + + // ---- Tab Process + + VBoxContainer *vbc_process = memnew(VBoxContainer); + vbc_process->set_h_size_flags(SIZE_EXPAND_FILL); + vbc_process->set_name(TTR("Post-Process")); + vbc_process->set_custom_minimum_size(Size2(0, feature_min_height)); + tabc_features->add_child(vbc_process); + + cbut_process = memnew(CheckButton); + cbut_process->set_text(TTR("Post-Process")); + vbc_process->add_child(cbut_process); + + // ------ Style + + HBoxContainer *hbc_style = memnew(HBoxContainer); + vbc_process->add_child(hbc_style); + + Label *lbl_style = memnew(Label); + lbl_style->set_text(TTR("Style")); + hbc_style->add_child(lbl_style); + + opt_style = memnew(OptionButton); + opt_style->add_item(TTR("Keep")); + opt_style->add_item(TTR("CamelCase to under_scored")); + opt_style->add_item(TTR("under_scored to CamelCase")); + hbc_style->add_child(opt_style); + + // ------ Case + + HBoxContainer *hbc_case = memnew(HBoxContainer); + vbc_process->add_child(hbc_case); + + Label *lbl_case = memnew(Label); + lbl_case->set_text(TTR("Case")); + hbc_case->add_child(lbl_case); + + opt_case = memnew(OptionButton); + opt_case->add_item(TTR("Keep")); + opt_case->add_item(TTR("To Lowercase")); + opt_case->add_item(TTR("To Uppercase")); + hbc_case->add_child(opt_case); + + // -- Preview + + HSeparator *sep_preview = memnew(HSeparator); + sep_preview->set_custom_minimum_size(Size2(10, 20)); + vbc->add_child(sep_preview); + + lbl_preview_title = memnew(Label); + lbl_preview_title->set_text(TTR("Preview")); + vbc->add_child(lbl_preview_title); + + lbl_preview = memnew(Label); + lbl_preview->set_text(""); + lbl_preview->add_color_override("font_color", Color(1, 0.5f, 0, 1)); + vbc->add_child(lbl_preview); + + // ---- Dialog related + + set_custom_minimum_size(Size2(383, 0)); + set_as_toplevel(true); + get_ok()->set_text(TTR("Rename")); + Button *but_reset = add_button(TTR("Reset")); + + eh.errfunc = _error_handler; + eh.userdata = this; + + // ---- Connections + + chk_collapse_features->connect("toggled", this, "_features_toggled"); + + // Substitite Buttons + + lne_search->connect("focus_entered", this, "_update_substitute"); + lne_search->connect("focus_exited", this, "_update_substitute"); + lne_replace->connect("focus_entered", this, "_update_substitute"); + lne_replace->connect("focus_exited", this, "_update_substitute"); + lne_prefix->connect("focus_entered", this, "_update_substitute"); + lne_prefix->connect("focus_exited", this, "_update_substitute"); + lne_suffix->connect("focus_entered", this, "_update_substitute"); + lne_suffix->connect("focus_exited", this, "_update_substitute"); + + // Preview + + lne_prefix->connect("text_changed", this, "_update_preview"); + lne_suffix->connect("text_changed", this, "_update_preview"); + lne_search->connect("text_changed", this, "_update_preview"); + lne_replace->connect("text_changed", this, "_update_preview"); + spn_count_start->connect("value_changed", this, "_update_preview_int"); + spn_count_step->connect("value_changed", this, "_update_preview_int"); + spn_count_padding->connect("value_changed", this, "_update_preview_int"); + opt_style->connect("item_selected", this, "_update_preview_int"); + opt_case->connect("item_selected", this, "_update_preview_int"); + cbut_substitute->connect("pressed", this, "_update_preview", varray("")); + cbut_regex->connect("pressed", this, "_update_preview", varray("")); + cbut_process->connect("pressed", this, "_update_preview", varray("")); + + but_reset->connect("pressed", this, "reset"); + + reset(); + _features_toggled(false); +} + +void RenameDialog::_bind_methods() { + + ClassDB::bind_method("_features_toggled", &RenameDialog::_features_toggled); + ClassDB::bind_method("_update_preview", &RenameDialog::_update_preview); + ClassDB::bind_method("_update_preview_int", &RenameDialog::_update_preview_int); + ClassDB::bind_method("_insert_text", &RenameDialog::_insert_text); + ClassDB::bind_method("_update_substitute", &RenameDialog::_update_substitute); + ClassDB::bind_method("reset", &RenameDialog::reset); + ClassDB::bind_method("rename", &RenameDialog::rename); +} + +void RenameDialog::_update_substitute() { + + LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(get_focus_owner()); + bool is_main_field = _is_main_field(focus_owner_line_edit); + + but_insert_name->set_disabled(!is_main_field); + but_insert_parent->set_disabled(!is_main_field); + but_insert_type->set_disabled(!is_main_field); + but_insert_scene->set_disabled(!is_main_field); + but_insert_root->set_disabled(!is_main_field); + but_insert_count->set_disabled(!is_main_field); + + // The focus mode seems to be reset when disabling/re-enabling + but_insert_name->set_focus_mode(FOCUS_NONE); + but_insert_parent->set_focus_mode(FOCUS_NONE); + but_insert_type->set_focus_mode(FOCUS_NONE); + but_insert_scene->set_focus_mode(FOCUS_NONE); + but_insert_root->set_focus_mode(FOCUS_NONE); + but_insert_count->set_focus_mode(FOCUS_NONE); +} + +void RenameDialog::_post_popup() { + + EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); + preview_node = NULL; + + Array selected_node_list = editor_selection->get_selected_nodes(); + ERR_FAIL_COND(selected_node_list.size() == 0); + + preview_node = selected_node_list[0]; + + _update_preview(); + _update_substitute(); +} + +void RenameDialog::_update_preview_int(int new_value) { + _update_preview(); +} + +void RenameDialog::_update_preview(String new_text) { + + if (lock_preview_update || preview_node == NULL) + return; + + has_errors = false; + add_error_handler(&eh); + + String new_name = _apply_rename(preview_node, spn_count_start->get_value()); + + if (!has_errors) { + + lbl_preview_title->set_text(TTR("Preview")); + lbl_preview->set_text(new_name); + + if (new_name == preview_node->get_name()) { + lbl_preview->add_color_override("font_color", Color(0, 0.5f, 0.25f, 1)); + } else { + lbl_preview->add_color_override("font_color", Color(0, 1, 0.5f, 1)); + } + } + + remove_error_handler(&eh); +} + +String RenameDialog::_apply_rename(const Node *node, int count) { + + String search = lne_search->get_text(); + String replace = lne_replace->get_text(); + String prefix = lne_prefix->get_text(); + String suffix = lne_suffix->get_text(); + String new_name = node->get_name(); + + if (cbut_substitute->is_pressed()) { + search = _substitute(search, node, count); + replace = _substitute(replace, node, count); + prefix = _substitute(prefix, node, count); + suffix = _substitute(suffix, node, count); + } + + if (cbut_regex->is_pressed()) { + + new_name = _regex(search, new_name, replace); + } else { + new_name = new_name.replace(search, replace); + } + + new_name = prefix + new_name + suffix; + + if (cbut_process->is_pressed()) { + new_name = _postprocess(new_name); + } + + return new_name; +} + +String RenameDialog::_substitute(const String &subject, const Node *node, int count) { + + String result = subject.replace("${COUNTER}", vformat("%0" + itos(spn_count_padding->get_value()) + "d", count)); + + if (node) { + result = result.replace("${NAME}", node->get_name()); + result = result.replace("${TYPE}", node->get_class()); + } + + int current = EditorNode::get_singleton()->get_editor_data().get_edited_scene(); + result = result.replace("${SCENE}", EditorNode::get_singleton()->get_editor_data().get_scene_title(current)); + + Node *root_node = SceneTree::get_singleton()->get_edited_scene_root(); + if (root_node) { + result = result.replace("${ROOT}", root_node->get_name()); + } + + Node *parent_node = node->get_parent(); + if (parent_node) { + if (node == root_node) { + // Can not substitute parent of root. + result = result.replace("${PARENT}", ""); + } else { + result = result.replace("${PARENT}", parent_node->get_name()); + } + } + + return result; +} + +void RenameDialog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type) { + + RenameDialog *self = (RenameDialog *)p_self; + String source_file(p_file); + + // Only show first error that is related to "regex" + if (self->has_errors || source_file.find("regex") < 0) + return; + + String err_str; + if (p_errorexp && p_errorexp[0]) { + err_str = p_errorexp; + } else { + err_str = p_error; + } + + self->has_errors = true; + self->lbl_preview_title->set_text(TTR("Error")); + self->lbl_preview->add_color_override("font_color", Color(1, 0.25f, 0, 1)); + self->lbl_preview->set_text(err_str); +} + +String RenameDialog::_regex(const String &pattern, const String &subject, const String &replacement) { + + RegEx regex(pattern); + + return regex.sub(subject, replacement, true); +} + +String RenameDialog::_postprocess(const String &subject) { + + int style_id = opt_style->get_selected(); + + String result = subject; + + if (style_id == 1) { + + // CamelCase to Under_Line + result = result.camelcase_to_underscore(true); + result = _regex("_+", result, "_"); + + } else if (style_id == 2) { + + // Under_Line to CamelCase + RegEx pattern("_+(.?)"); + Array matches = pattern.search_all(result); + + // _ name would become empty. Ignore + if (matches.size() && result != "_") { + String buffer; + int start = 0; + int end = 0; + for (int i = 0; i < matches.size(); ++i) { + start = ((Ref<RegExMatch>)matches[i])->get_start(1); + buffer += result.substr(end, start - end - 1); + buffer += result.substr(start, 1).to_upper(); + end = start + 1; + } + buffer += result.substr(end, result.size() - (end + 1)); + result = buffer.replace("_", "").capitalize(); + } + } + + int case_id = opt_case->get_selected(); + + if (case_id == 1) { + // To Lowercase + result = result.to_lower(); + } else if (case_id == 2) { + // To Upercase + result = result.to_upper(); + } + + return result; +} + +void RenameDialog::_iterate_scene(const Node *node, const Array &selection, int *counter) { + + if (!node) + return; + + if (selection.has(node)) { + + String new_name = _apply_rename(node, *counter); + + if (node->get_name() != new_name) { + Pair<NodePath, String> rename_item; + rename_item.first = node->get_path(); + rename_item.second = new_name; + to_rename.push_back(rename_item); + } + + *counter += spn_count_step->get_value(); + } + + int *cur_counter = counter; + int level_counter = spn_count_start->get_value(); + + if (chk_per_level_counter->is_pressed()) { + cur_counter = &level_counter; + } + + for (int i = 0; i < node->get_child_count(); ++i) { + _iterate_scene(node->get_child(i), selection, cur_counter); + } +} + +void RenameDialog::rename() { + + // Editor selection is not ordered via scene tree. Instead iterate + // over scene tree until all selected nodes are found in order. + + EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); + Array selected_node_list = editor_selection->get_selected_nodes(); + Node *root_node = SceneTree::get_singleton()->get_edited_scene_root(); + + global_count = spn_count_start->get_value(); + to_rename.clear(); + + // Forward recursive as opposed to the actual renaming. + _iterate_scene(root_node, selected_node_list, &global_count); + + if (undo_redo && !to_rename.empty()) { + + undo_redo->create_action(TTR("Batch Rename")); + + // Make sure to iterate reversed so that child nodes will find parents. + for (int i = to_rename.size() - 1; i >= 0; --i) { + + Node *n = root_node->get_node(to_rename[i].first); + const String &new_name = to_rename[i].second; + + if (!n) { + ERR_PRINTS("Skipping missing node: " + to_rename[i].first.get_concatenated_subnames()); + continue; + } + + scene_tree_editor->emit_signal("node_prerename", n, new_name); + undo_redo->add_do_method(scene_tree_editor, "_rename_node", n->get_instance_id(), new_name); + undo_redo->add_undo_method(scene_tree_editor, "_rename_node", n->get_instance_id(), n->get_name()); + } + + undo_redo->commit_action(); + } +} + +void RenameDialog::reset() { + + lock_preview_update = true; + + lne_prefix->clear(); + lne_suffix->clear(); + lne_search->clear(); + lne_replace->clear(); + + cbut_substitute->set_pressed(false); + cbut_regex->set_pressed(false); + cbut_process->set_pressed(false); + + chk_per_level_counter->set_pressed(true); + + spn_count_start->set_value(1); + spn_count_step->set_value(1); + spn_count_padding->set_value(1); + + opt_style->select(0); + opt_case->select(0); + + lock_preview_update = false; + _update_preview(); +} + +bool RenameDialog::_is_main_field(LineEdit *line_edit) { + return line_edit && + (line_edit == lne_search || line_edit == lne_replace || line_edit == lne_prefix || line_edit == lne_suffix); +} + +void RenameDialog::_insert_text(String text) { + + LineEdit *focus_owner = Object::cast_to<LineEdit>(get_focus_owner()); + + if (_is_main_field(focus_owner)) { + focus_owner->selection_delete(); + focus_owner->append_at_cursor(text); + _update_preview(); + } +} + +void RenameDialog::_features_toggled(bool pressed) { + if (pressed) { + tabc_features->show(); + } else { + tabc_features->hide(); + } + + // Adjust to minimum size in y + Size2i size = get_size(); + size.y = 0; + set_size(size); +} diff --git a/editor/rename_dialog.h b/editor/rename_dialog.h new file mode 100644 index 0000000000..c5ebc30c0c --- /dev/null +++ b/editor/rename_dialog.h @@ -0,0 +1,119 @@ +/*************************************************************************/ +/* rename_dialog.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 RENAME_DIALOG_H +#define RENAME_DIALOG_H + +#include "scene/gui/check_box.h" +#include "scene/gui/check_button.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/option_button.h" +#include "scene/gui/spin_box.h" + +#include "editor/scene_tree_editor.h" +#include "undo_redo.h" + +/** +@author Blazej Floch +*/ + +class RenameDialog : public ConfirmationDialog { + + GDCLASS(RenameDialog, ConfirmationDialog); + + virtual void ok_pressed() { rename(); }; + void _cancel_pressed(){}; + void _features_toggled(bool pressed); + void _insert_text(String text); + void _update_substitute(); + bool _is_main_field(LineEdit *line_edit); + + void _iterate_scene(const Node *node, const Array &selection, int *count); + String _apply_rename(const Node *node, int count); + String _substitute(const String &subject, const Node *node, int count); + String _regex(const String &pattern, const String &subject, const String &replacement); + String _postprocess(const String &subject); + void _update_preview(String new_text = ""); + void _update_preview_int(int new_value = 0); + static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type); + + SceneTreeEditor *scene_tree_editor; + UndoRedo *undo_redo; + int global_count; + + LineEdit *lne_search; + LineEdit *lne_replace; + LineEdit *lne_prefix; + LineEdit *lne_suffix; + + TabContainer *tabc_features; + + CheckButton *cbut_substitute; + CheckButton *cbut_regex; + CheckButton *cbut_process; + CheckBox *chk_per_level_counter; + + Button *but_insert_name; + Button *but_insert_parent; + Button *but_insert_type; + Button *but_insert_scene; + Button *but_insert_root; + Button *but_insert_count; + + SpinBox *spn_count_start; + SpinBox *spn_count_step; + SpinBox *spn_count_padding; + + OptionButton *opt_style; + OptionButton *opt_case; + + Label *lbl_preview_title; + Label *lbl_preview; + + List<Pair<NodePath, String> > to_rename; + Node *preview_node; + bool lock_preview_update; + ErrorHandlerList eh; + bool has_errors; + +protected: + void _notification(int p_what){}; + static void _bind_methods(); + virtual void _post_popup(); + +public: + void reset(); + void rename(); + + RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_undo_redo = NULL); + ~RenameDialog(){}; +}; + +#endif diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index ba661813d6..32b4e7f962 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -73,7 +73,11 @@ void SceneTreeDock::_unhandled_key_input(Ref<InputEvent> p_event) { if (!p_event->is_pressed() || p_event->is_echo()) return; - if (ED_IS_SHORTCUT("scene_tree/add_child_node", p_event)) { + if (ED_IS_SHORTCUT("scene_tree/batch_rename", p_event)) { + _tool_selected(TOOL_BATCH_RENAME); + } else if (ED_IS_SHORTCUT("scene_tree/rename", p_event)) { + _tool_selected(TOOL_RENAME); + } else if (ED_IS_SHORTCUT("scene_tree/add_child_node", p_event)) { _tool_selected(TOOL_NEW); } else if (ED_IS_SHORTCUT("scene_tree/instance_scene", p_event)) { _tool_selected(TOOL_INSTANCE); @@ -109,7 +113,7 @@ void SceneTreeDock::instance(const String &p_file) { Node *parent = scene_tree->get_selected(); if (!parent) { - Node *parent = edited_scene; + parent = edited_scene; }; if (!edited_scene) { @@ -285,6 +289,19 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { switch (p_tool) { + case TOOL_BATCH_RENAME: { + Tree *tree = scene_tree->get_scene_tree(); + if (tree->is_anything_selected()) { + rename_dialog->popup_centered(); + } + } break; + case TOOL_RENAME: { + Tree *tree = scene_tree->get_scene_tree(); + if (tree->is_anything_selected()) { + tree->grab_focus(); + tree->edit_selected(); + } + } break; case TOOL_NEW: { String preferred = ""; @@ -724,7 +741,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (node) { node->set_scene_inherited_state(Ref<SceneState>()); scene_tree->update_tree(); - EditorNode::get_singleton()->get_property_editor()->update_tree(); + EditorNode::get_singleton()->get_inspector()->update_tree(); } } } break; @@ -1877,6 +1894,9 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); menu->add_separator(); + menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME); + } else { // multi select + menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/batch_rename"), TOOL_BATCH_RENAME); } menu->add_icon_shortcut(get_icon("Reload", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/change_node_type"), TOOL_REPLACE); menu->add_separator(); @@ -1916,6 +1936,12 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); } + + if (selection.size() > 1) { + //this is not a commonly used action, it makes no sense for it to be where it was nor always present. + menu->add_separator(); + menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/batch_rename"), TOOL_BATCH_RENAME); + } menu->add_separator(); menu->add_icon_shortcut(get_icon("Remove", "EditorIcons"), ED_SHORTCUT("scene_tree/delete", TTR("Delete Node(s)"), KEY_DELETE), TOOL_ERASE); menu->set_size(Size2(1, 1)); @@ -2055,6 +2081,8 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel filter_hbc->add_constant_override("separate", 0); ToolButton *tb; + ED_SHORTCUT("scene_tree/rename", TTR("Rename")); + ED_SHORTCUT("scene_tree/batch_rename", TTR("Batch Rename"), KEY_MASK_CMD | KEY_F2); ED_SHORTCUT("scene_tree/add_child_node", TTR("Add Child Node"), KEY_MASK_CMD | KEY_A); ED_SHORTCUT("scene_tree/instance_scene", TTR("Instance Child Scene")); ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type")); @@ -2153,6 +2181,9 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel add_child(create_dialog); create_dialog->connect("create", this, "_create"); + rename_dialog = memnew(RenameDialog(scene_tree, &editor_data->get_undo_redo())); + add_child(rename_dialog); + script_create_dialog = memnew(ScriptCreateDialog); add_child(script_create_dialog); script_create_dialog->connect("script_created", this, "_script_created"); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index a4f36e31ee..ed13e063bb 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -36,6 +36,7 @@ #include "editor/editor_data.h" #include "editor/editor_sub_scene.h" #include "editor/groups_editor.h" +#include "editor/rename_dialog.h" #include "editor/reparent_dialog.h" #include "editor/script_create_dialog.h" #include "scene/animation/animation_player.h" @@ -58,6 +59,8 @@ class SceneTreeDock : public VBoxContainer { TOOL_NEW, TOOL_INSTANCE, + TOOL_RENAME, + TOOL_BATCH_RENAME, TOOL_REPLACE, TOOL_ATTACH_SCRIPT, TOOL_CLEAR_SCRIPT, @@ -90,6 +93,7 @@ class SceneTreeDock : public VBoxContainer { int current_option; CreateDialog *create_dialog; + RenameDialog *rename_dialog; ToolButton *button_add; ToolButton *button_instance; diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 64d278c0c5..dd79ae63d6 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -70,8 +70,18 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i } else if (p_id == BUTTON_VISIBILITY) { undo_redo->create_action(TTR("Toggle Visible")); - undo_redo->add_do_method(this, "toggle_visible", n); - undo_redo->add_undo_method(this, "toggle_visible", n); + _toggle_visible(n); + List<Node *> selection = editor_selection->get_selected_node_list(); + if (selection.size() > 1) { + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + Node *nv = E->get(); + ERR_FAIL_COND(!nv); + if (nv == n) { + continue; + } + _toggle_visible(nv); + } + } undo_redo->commit_action(); } else if (p_id == BUTTON_LOCK) { @@ -118,33 +128,13 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i } } void SceneTreeEditor::_toggle_visible(Node *p_node) { - if (p_node->is_class("Spatial")) { - bool v = bool(p_node->call("is_visible")); - p_node->call("set_visible", !v); - } else if (p_node->is_class("CanvasItem")) { + if (p_node->has_method("is_visible") && p_node->has_method("set_visible")) { bool v = bool(p_node->call("is_visible")); - if (v) { - p_node->call("hide"); - } else { - p_node->call("show"); - } + undo_redo->add_do_method(p_node, "set_visible", !v); + undo_redo->add_undo_method(p_node, "set_visible", v); } } -void SceneTreeEditor::toggle_visible(Node *p_node) { - _toggle_visible(p_node); - List<Node *> selection = editor_selection->get_selected_node_list(); - if (selection.size() > 1) { - for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { - Node *nv = E->get(); - ERR_FAIL_COND(!nv); - if (nv == p_node) { - continue; - } - _toggle_visible(nv); - } - } -} bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { if (!p_node) @@ -968,8 +958,6 @@ void SceneTreeEditor::_bind_methods() { ClassDB::bind_method("_cell_collapsed", &SceneTreeEditor::_cell_collapsed); ClassDB::bind_method("_rmb_select", &SceneTreeEditor::_rmb_select); ClassDB::bind_method("_warning_changed", &SceneTreeEditor::_warning_changed); - ClassDB::bind_method("_toggle_visible", &SceneTreeEditor::_toggle_visible); - ClassDB::bind_method("toggle_visible", &SceneTreeEditor::toggle_visible); ClassDB::bind_method("_node_script_changed", &SceneTreeEditor::_node_script_changed); ClassDB::bind_method("_node_visibility_changed", &SceneTreeEditor::_node_visibility_changed); diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h index b63eb2a1f0..896fd6c431 100644 --- a/editor/scene_tree_editor.h +++ b/editor/scene_tree_editor.h @@ -70,8 +70,6 @@ class SceneTreeEditor : public Control { void _compute_hash(Node *p_node, uint64_t &hash); - void toggle_visible(Node *p_node); - bool _add_nodes(Node *p_node, TreeItem *p_parent); void _test_update_tree(); void _update_tree(); diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index a83de1627d..62848a6035 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -872,7 +872,7 @@ void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType reason->add_color_override("font_color", get_color("success_color", "Editor")); } reason->set_text(p_reason); - reason->set_tooltip(p_reason); + reason->set_tooltip(p_reason.word_wrap(80)); } void ScriptEditorDebugger::_performance_select() { @@ -1851,7 +1851,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { ppeer = Ref<PacketPeerStream>(memnew(PacketPeerStream)); ppeer->set_input_buffer_max_size(1024 * 1024 * 8); //8mb should be enough editor = p_editor; - editor->get_property_editor()->connect("object_id_selected", this, "_scene_tree_property_select_object"); + editor->get_inspector()->connect("object_id_selected", this, "_scene_tree_property_select_object"); tabs = memnew(TabContainer); tabs->set_tab_align(TabContainer::ALIGN_LEFT); @@ -1877,6 +1877,9 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { reason->set_text(""); hbc->add_child(reason); reason->set_h_size_flags(SIZE_EXPAND_FILL); + reason->set_autowrap(true); + reason->set_max_lines_visible(3); + reason->set_mouse_filter(Control::MOUSE_FILTER_PASS); hbc->add_child(memnew(VSeparator)); @@ -1936,7 +1939,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { 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->get_property_tree()->set_column_title(0, TTR("Variable")); inspector->set_enable_capitalize_paths(false); inspector->set_read_only(true); inspector->connect("object_id_selected", this, "_scene_tree_property_select_object"); diff --git a/editor/translations/editor.pot b/editor/translations/editor.pot index 61d67d7089..687c517180 100644 --- a/editor/translations/editor.pot +++ b/editor/translations/editor.pot @@ -3261,19 +3261,19 @@ msgid "Download for this asset is already in progress!" msgstr "" #: editor/plugins/asset_library_editor_plugin.cpp -msgid "first" +msgid "First" msgstr "" #: editor/plugins/asset_library_editor_plugin.cpp -msgid "prev" +msgid "Previous" msgstr "" #: editor/plugins/asset_library_editor_plugin.cpp -msgid "next" +msgid "Next" msgstr "" #: editor/plugins/asset_library_editor_plugin.cpp -msgid "last" +msgid "Last" msgstr "" #: editor/plugins/asset_library_editor_plugin.cpp |