diff options
Diffstat (limited to 'editor')
42 files changed, 919 insertions, 93 deletions
diff --git a/editor/SCsub b/editor/SCsub index 75ec422bd5..8a0e36b4a3 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -475,6 +475,7 @@ if env['tools']: SConscript('plugins/SCsub') lib = env.Library("editor", env.editor_sources) + env.NoCache(lib) env.Prepend(LIBS=[lib]) Export('env') diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index 290cb1be42..da41ea87ce 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "editor_about.h" +#include "editor_node.h" #include "authors.gen.h" #include "donors.gen.h" @@ -35,6 +36,20 @@ #include "version.h" #include "version_hash.gen.h" +void EditorAbout::_notification(int p_what) { + + switch (p_what) { + + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + + Ref<Font> font = EditorNode::get_singleton()->get_gui_base()->get_font("source", "EditorFonts"); + _tpl_text->add_font_override("font", font); + _license_text->add_font_override("font", font); + } break; + } +} + void EditorAbout::_license_tree_selected() { TreeItem *selected = _tpl_tree->get_selected(); @@ -52,7 +67,7 @@ TextureRect *EditorAbout::get_logo() const { return _logo; } -ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<String> &p_sections, const char **p_src[]) { +ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<String> &p_sections, const char **p_src[], const int p_flag_single_column) { ScrollContainer *sc = memnew(ScrollContainer); sc->set_name(p_name); @@ -64,6 +79,7 @@ ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<St for (int i = 0; i < p_sections.size(); i++) { + bool single_column = p_flag_single_column & 1 << i; const char **names_ptr = p_src[i]; if (*names_ptr) { @@ -72,17 +88,16 @@ ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<St vbc->add_child(lbl); ItemList *il = memnew(ItemList); - il->set_max_columns(16); il->set_h_size_flags(Control::SIZE_EXPAND_FILL); il->set_same_column_width(true); il->set_auto_height(true); + il->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + il->add_constant_override("hseparation", 16 * EDSCALE); while (*names_ptr) { il->add_item(String::utf8(*names_ptr++), NULL, false); } + il->set_max_columns(il->get_item_count() < 4 || single_column ? 1 : 16); vbc->add_child(il); - if (il->get_item_count() == 2) { - il->set_fixed_column_width(200 * EDSCALE); - } HSeparator *hs = memnew(HSeparator); hs->set_modulate(Color(0, 0, 0, 0)); @@ -134,7 +149,7 @@ EditorAbout::EditorAbout() { dev_sections.push_back(TTR("Project Manager")); dev_sections.push_back(TTR("Developers")); const char **dev_src[] = { dev_founders, dev_lead, dev_manager, dev_names }; - tc->add_child(_populate_list(TTR("Authors"), dev_sections, dev_src)); + tc->add_child(_populate_list(TTR("Authors"), dev_sections, dev_src, 1)); // Donors @@ -146,18 +161,18 @@ EditorAbout::EditorAbout() { donor_sections.push_back(TTR("Silver Donors")); donor_sections.push_back(TTR("Bronze Donors")); const char **donor_src[] = { donor_s_plat, donor_s_gold, donor_s_mini, donor_gold, donor_silver, donor_bronze }; - tc->add_child(_populate_list(TTR("Donors"), donor_sections, donor_src)); + tc->add_child(_populate_list(TTR("Donors"), donor_sections, donor_src, 3)); // License - TextEdit *license = memnew(TextEdit); - license->set_name(TTR("License")); - license->set_h_size_flags(Control::SIZE_EXPAND_FILL); - license->set_v_size_flags(Control::SIZE_EXPAND_FILL); - license->set_wrap(true); - license->set_readonly(true); - license->set_text(String::utf8(about_license)); - tc->add_child(license); + _license_text = memnew(TextEdit); + _license_text->set_name(TTR("License")); + _license_text->set_h_size_flags(Control::SIZE_EXPAND_FILL); + _license_text->set_v_size_flags(Control::SIZE_EXPAND_FILL); + _license_text->set_wrap(true); + _license_text->set_readonly(true); + _license_text->set_text(String::utf8(about_license)); + tc->add_child(_license_text); // Thirdparty License diff --git a/editor/editor_about.h b/editor/editor_about.h index d455b1f074..ce29027f05 100644 --- a/editor/editor_about.h +++ b/editor/editor_about.h @@ -52,13 +52,15 @@ class EditorAbout : public AcceptDialog { private: void _license_tree_selected(); - ScrollContainer *_populate_list(const String &p_name, const List<String> &p_sections, const char **p_src[]); + ScrollContainer *_populate_list(const String &p_name, const List<String> &p_sections, const char **p_src[], const int p_flag_single_column = 0); Tree *_tpl_tree; + TextEdit *_license_text; TextEdit *_tpl_text; TextureRect *_logo; protected: + void _notification(int p_what); static void _bind_methods(); public: diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index f8b9425a4e..eb5af2eaeb 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -197,6 +197,9 @@ Vector<String> EditorFileDialog::get_selected_files() const { void EditorFileDialog::update_dir() { dir->set_text(dir_access->get_current_dir()); + + // Disable "Open" button only when we in selecting file(s) mode or open dir mode. + get_ok()->set_disabled(_is_open_should_be_disabled()); } void EditorFileDialog::_dir_entered(String p_dir) { @@ -452,6 +455,28 @@ void EditorFileDialog::_item_selected(int p_item) { file->set_text(d["name"]); _request_single_thumbnail(get_current_dir().plus_file(get_current_file())); } + + get_ok()->set_disabled(_is_open_should_be_disabled()); +} + +void EditorFileDialog::_items_clear_selection() { + + item_list->unselect_all(); + + // If nothing is selected, then block Open button. + switch (mode) { + + case MODE_OPEN_FILE: + case MODE_OPEN_FILES: + get_ok()->set_text(TTR("Open")); + get_ok()->set_disabled(item_list->is_anything_selected() == false); + break; + + case MODE_OPEN_DIR: + get_ok()->set_disabled(false); + get_ok()->set_text(TTR("Select Current Folder")); + break; + } } void EditorFileDialog::_push_history() { @@ -487,6 +512,26 @@ void EditorFileDialog::_item_dc_selected(int p_item) { } } +bool EditorFileDialog::_is_open_should_be_disabled() { + + if (mode == MODE_OPEN_ANY || mode == MODE_SAVE_FILE) + return false; + + Vector<int> items = item_list->get_selected_items(); + if (items.size() == 0) + return true; + + for (int i = 0; i < items.size(); i++) { + + Dictionary d = item_list->get_item_metadata(items.get(i)); + + if (((mode == MODE_OPEN_FILE || mode == MODE_OPEN_FILES) && d["dir"]) || (mode == MODE_OPEN_DIR && !d["dir"])) + return true; + } + + return false; +} + void EditorFileDialog::update_file_list() { int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); @@ -541,7 +586,7 @@ void EditorFileDialog::update_file_list() { while ((item = dir_access->get_next(&isdir)) != "") { - if (item == ".") + if (item == "." || item == "..") continue; ishidden = dir_access->current_is_hidden(); @@ -554,11 +599,6 @@ void EditorFileDialog::update_file_list() { } } - if (dirs.find("..") == NULL) { - //may happen if lacking permissions - dirs.push_back(".."); - } - dirs.sort_custom<NaturalNoCaseComparator>(); files.sort_custom<NaturalNoCaseComparator>(); @@ -681,6 +721,7 @@ void EditorFileDialog::update_file_list() { favorite->set_pressed(false); fav_up->set_disabled(true); fav_down->set_disabled(true); + get_ok()->set_disabled(_is_open_should_be_disabled()); for (int i = 0; i < favorites->get_item_count(); i++) { if (favorites->get_item_metadata(i) == base_dir) { favorites->select(i); @@ -1139,6 +1180,7 @@ void EditorFileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_unhandled_input"), &EditorFileDialog::_unhandled_input); ClassDB::bind_method(D_METHOD("_item_selected"), &EditorFileDialog::_item_selected); + ClassDB::bind_method(D_METHOD("_items_clear_selection"), &EditorFileDialog::_items_clear_selection); ClassDB::bind_method(D_METHOD("_item_db_selected"), &EditorFileDialog::_item_dc_selected); ClassDB::bind_method(D_METHOD("_dir_entered"), &EditorFileDialog::_dir_entered); ClassDB::bind_method(D_METHOD("_file_entered"), &EditorFileDialog::_file_entered); @@ -1415,6 +1457,7 @@ EditorFileDialog::EditorFileDialog() { //cancel->connect("pressed", this,"_cancel_pressed"); item_list->connect("item_selected", this, "_item_selected", varray(), CONNECT_DEFERRED); item_list->connect("item_activated", this, "_item_db_selected", varray()); + item_list->connect("nothing_selected", this, "_items_clear_selection"); dir->connect("text_entered", this, "_dir_entered"); file->connect("text_entered", this, "_file_entered"); filter->connect("item_selected", this, "_filter_selected"); diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h index 4dc2947292..0599d222f3 100644 --- a/editor/editor_file_dialog.h +++ b/editor/editor_file_dialog.h @@ -143,6 +143,7 @@ private: void _recent_selected(int p_idx); void _item_selected(int p_item); + void _items_clear_selection(); void _item_dc_selected(int p_item); void _select_drive(int p_idx); @@ -172,6 +173,8 @@ private: void _unhandled_input(const Ref<InputEvent> &p_event); + bool _is_open_should_be_disabled(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index 7e20077fd6..8aca007e6b 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -134,14 +134,14 @@ void editor_register_fonts(Ref<Theme> p_theme) { //Ref<BitmapFont> doc_title_font = make_font(_bi_font_doc_title_font_height,_bi_font_doc_title_font_ascent,0,_bi_font_doc_title_font_charcount,_bi_font_doc_title_font_characters,p_theme->get_icon("DocTitleFont","EditorIcons")); //Ref<BitmapFont> doc_code_font = make_font(_bi_font_doc_code_font_height,_bi_font_doc_code_font_ascent,0,_bi_font_doc_code_font_charcount,_bi_font_doc_code_font_characters,p_theme->get_icon("DocCodeFont","EditorIcons")); - MAKE_DEFAULT_FONT(df_doc_title, int(EDITOR_DEF("text_editor/help/help_title_font_size", 16)) * EDSCALE); + MAKE_DEFAULT_FONT(df_doc_title, int(EDITOR_DEF("text_editor/help/help_title_font_size", 23)) * EDSCALE); - MAKE_DEFAULT_FONT(df_doc, int(EDITOR_DEF("text_editor/help/help_font_size", 14)) * EDSCALE); + MAKE_DEFAULT_FONT(df_doc, int(EDITOR_DEF("text_editor/help/help_font_size", 15)) * EDSCALE); p_theme->set_font("doc", "EditorFonts", df_doc); p_theme->set_font("doc_title", "EditorFonts", df_doc_title); - MAKE_DEFAULT_FONT(df_rulers, int(EDITOR_DEF("canvas_item_editor/rulers", 8)) * EDSCALE); + MAKE_DEFAULT_FONT(df_rulers, 8 * EDSCALE); p_theme->set_font("rulers", "EditorFonts", df_rulers); Ref<DynamicFont> df_code; @@ -154,7 +154,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { Ref<DynamicFont> df_doc_code; df_doc_code.instance(); - df_doc_code->set_size(int(EDITOR_DEF("text_editor/help/help_source_font_size", 16)) * EDSCALE); + df_doc_code->set_size(int(EDITOR_DEF("text_editor/help/help_source_font_size", 14)) * EDSCALE); df_doc_code->set_spacing(DynamicFont::SPACING_TOP, -EDSCALE); df_doc_code->set_spacing(DynamicFont::SPACING_BOTTOM, -EDSCALE); df_doc_code->set_font_data(dfmono); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index a32ade3b71..b5c7187b81 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -289,6 +289,7 @@ void EditorNode::_notification(int p_what) { _editor_select(EDITOR_3D); _update_debug_options(); + _load_docks(); } if (p_what == MainLoop::NOTIFICATION_WM_FOCUS_IN) { @@ -1392,6 +1393,14 @@ void EditorNode::_property_editor_back() { _edit_current(); } +void EditorNode::_menu_collapseall() { + property_editor->collapse_all_parent_nodes(); +} + +void EditorNode::_menu_expandall() { + property_editor->expand_all_parent_nodes(); +} + void EditorNode::_save_default_environment() { Ref<Environment> fallback = get_tree()->get_root()->get_world()->get_fallback_environment(); @@ -1466,6 +1475,7 @@ void EditorNode::_edit_current() { object_menu->set_disabled(true); bool capitalize = bool(EDITOR_DEF("interface/editor/capitalize_properties", true)); + bool expandall = bool(EDITOR_DEF("interface/editor/expand_all_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); @@ -1537,6 +1547,10 @@ void EditorNode::_edit_current() { property_editor->set_enable_capitalize_paths(capitalize); } + if (property_editor->is_expand_all_properties_enabled() != expandall) { + property_editor->set_use_folding(expandall == false); + } + /* Take care of PLUGIN EDITOR */ EditorPlugin *main_plugin = editor_data.get_editor(current_obj); @@ -1596,6 +1610,9 @@ void EditorNode::_edit_current() { 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(); @@ -2226,6 +2243,14 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { _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); @@ -3110,6 +3135,10 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b push_item(new_scene); + if (!restoring_scenes) { + save_layout(); + } + return OK; } @@ -3630,6 +3659,7 @@ void EditorNode::_save_docks() { config.instance(); _save_docks_to_config(config, "docks"); + _save_open_scenes_to_config(config, "EditorNode"); editor_data.get_plugin_window_layout(config); config->save(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg")); @@ -3680,6 +3710,18 @@ void EditorNode::_save_docks_to_config(Ref<ConfigFile> p_layout, const String &p } } +void EditorNode::_save_open_scenes_to_config(Ref<ConfigFile> p_layout, const String &p_section) { + Array scenes; + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + String path = editor_data.get_scene_path(i); + if (path == "") { + continue; + } + scenes.push_back(path); + } + p_layout->set_value(p_section, "open_scenes", scenes); +} + void EditorNode::save_layout() { dock_drag_timer->start(); @@ -3704,6 +3746,7 @@ void EditorNode::_load_docks() { } _load_docks_from_config(config, "docks"); + _load_open_scenes_from_config(config, "EditorNode"); editor_data.set_plugin_window_layout(config); } @@ -3850,6 +3893,25 @@ 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))) { + return; + } + + if (!p_layout->has_section(p_section) || !p_layout->has_section_key(p_section, "open_scenes")) { + return; + } + + restoring_scenes = true; + + Array scenes = p_layout->get_value(p_section, "open_scenes"); + for (int i = 0; i < scenes.size(); i++) { + load_scene(scenes[i]); + } + + restoring_scenes = false; +} + void EditorNode::_update_layouts_menu() { editor_layouts->clear(); @@ -3948,6 +4010,8 @@ void EditorNode::_scene_tab_closed(int p_tab) { } else { _discard_changes(); } + + save_layout(); _update_scene_tabs(); } @@ -4645,6 +4709,7 @@ EditorNode::EditorNode() { changing_scene = false; _initializing_addons = false; docks_visible = true; + restoring_scenes = false; scene_distraction = false; script_distraction = false; @@ -5262,13 +5327,14 @@ EditorNode::EditorNode() { } scene_tree_dock = memnew(SceneTreeDock(this, scene_root, editor_selection, editor_data)); - scene_tree_dock->set_name(TTR("Scene")); dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(scene_tree_dock); + 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(TTR("Inspector")); // Properties? + 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); @@ -5376,11 +5442,11 @@ EditorNode::EditorNode() { property_editor = memnew(PropertyEditor); property_editor->set_autoclear(true); property_editor->set_show_categories(true); - property_editor->set_use_folding(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/expand_all_properties", false)) == false); property_editor->hide_top_label(); property_editor->register_text_enter(search_box); @@ -5391,28 +5457,31 @@ EditorNode::EditorNode() { import_dock = memnew(ImportDock); dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(import_dock); - import_dock->set_name(TTR("Import")); + dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(import_dock->get_index(), TTR("Import")); bool use_single_dock_column = (OS::get_singleton()->get_screen_size(OS::get_singleton()->get_current_screen()).x < 1200); node_dock = memnew(NodeDock); if (use_single_dock_column) { dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(node_dock); + dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(node_dock->get_index(), TTR("Node")); } else { dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(node_dock); + dock_slot[DOCK_SLOT_RIGHT_BL]->set_tab_title(node_dock->get_index(), TTR("Node")); } filesystem_dock = memnew(FileSystemDock(this)); - filesystem_dock->set_name(TTR("FileSystem")); filesystem_dock->set_display_mode(int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode"))); if (use_single_dock_column) { dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(filesystem_dock); + dock_slot[DOCK_SLOT_RIGHT_BL]->set_tab_title(filesystem_dock->get_index(), TTR("FileSystem")); left_r_vsplit->hide(); dock_slot[DOCK_SLOT_LEFT_UR]->hide(); dock_slot[DOCK_SLOT_LEFT_BR]->hide(); } else { dock_slot[DOCK_SLOT_LEFT_UR]->add_child(filesystem_dock); + dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(filesystem_dock->get_index(), TTR("FileSystem")); } filesystem_dock->connect("open", this, "open_request"); filesystem_dock->connect("instance", this, "_instance_request"); @@ -5421,9 +5490,9 @@ EditorNode::EditorNode() { overridden_default_layout = -1; default_layout.instance(); - default_layout->set_value(docks_section, "dock_3", TTR("FileSystem")); - default_layout->set_value(docks_section, "dock_5", TTR("Scene") + "," + TTR("Import")); - default_layout->set_value(docks_section, "dock_6", TTR("Inspector") + "," + TTR("Node")); + default_layout->set_value(docks_section, "dock_3", "FileSystem"); + default_layout->set_value(docks_section, "dock_5", "Scene,Import"); + default_layout->set_value(docks_section, "dock_6", "Inspector,Node"); for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) default_layout->set_value(docks_section, "dock_hsplit_" + itos(i + 1), 0); @@ -5624,6 +5693,7 @@ EditorNode::EditorNode() { editor_plugin_screen = NULL; editor_plugins_over = memnew(EditorPluginList); + editor_plugins_force_over = memnew(EditorPluginList); editor_plugins_force_input_forwarding = memnew(EditorPluginList); _edit_current(); @@ -5716,8 +5786,6 @@ EditorNode::EditorNode() { _initializing_addons = false; } - _load_docks(); - FileAccess::set_file_close_fail_notify_callback(_file_access_close_error_notify); waiting_for_first_scan = true; @@ -5748,6 +5816,7 @@ EditorNode::~EditorNode() { memdelete(EditorHelp::get_doc_data()); memdelete(editor_selection); memdelete(editor_plugins_over); + memdelete(editor_plugins_force_over); memdelete(editor_plugins_force_input_forwarding); memdelete(file_server); memdelete(progress_hb); @@ -5801,10 +5870,17 @@ bool EditorPluginList::forward_spatial_gui_input(Camera *p_camera, const Ref<Inp return discard; } -void EditorPluginList::forward_draw_over_canvas(Control *p_canvas) { +void EditorPluginList::forward_draw_over_viewport(Control *p_overlay) { + + for (int i = 0; i < plugins_list.size(); i++) { + plugins_list[i]->forward_draw_over_viewport(p_overlay); + } +} + +void EditorPluginList::forward_force_draw_over_viewport(Control *p_overlay) { for (int i = 0; i < plugins_list.size(); i++) { - plugins_list[i]->forward_draw_over_canvas(p_canvas); + plugins_list[i]->forward_force_draw_over_viewport(p_overlay); } } diff --git a/editor/editor_node.h b/editor/editor_node.h index a2b4a0a049..658d5dc0ae 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -153,6 +153,9 @@ private: OBJECT_REQUEST_HELP, RUN_PLAY, + COLLAPSE_ALL, + EXPAND_ALL, + RUN_STOP, RUN_PLAY_SCENE, RUN_PLAY_NATIVE, @@ -378,6 +381,7 @@ private: Vector<EditorPlugin *> editor_plugins; EditorPlugin *editor_plugin_screen; EditorPluginList *editor_plugins_over; + EditorPluginList *editor_plugins_force_over; EditorPluginList *editor_plugins_force_input_forwarding; EditorHistory editor_history; @@ -426,6 +430,9 @@ private: void _property_editor_forward(); void _property_editor_back(); + void _menu_collapseall(); + void _menu_expandall(); + void _select_history(int p_idx); void _prepare_history(); @@ -559,6 +566,10 @@ private: void _load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section); void _update_dock_slots_visibility(); + bool restoring_scenes; + void _save_open_scenes_to_config(Ref<ConfigFile> p_layout, const String &p_section); + void _load_open_scenes_from_config(Ref<ConfigFile> p_layout, const String &p_section); + void _update_layouts_menu(); void _layout_menu_option(int p_id); @@ -636,6 +647,7 @@ public: EditorPlugin *get_editor_plugin_screen() { return editor_plugin_screen; } 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; } @@ -820,7 +832,8 @@ public: void edit(Object *p_object); bool forward_gui_input(const Ref<InputEvent> &p_event); bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event, bool serve_when_force_input_enabled); - void forward_draw_over_canvas(Control *p_canvas); + void forward_draw_over_viewport(Control *p_overlay); + void forward_force_draw_over_viewport(Control *p_overlay); void add_plugin(EditorPlugin *p_plugin); void clear(); bool empty(); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 38e8b301b7..c1fbcde6ac 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -375,6 +375,12 @@ void EditorPlugin::set_input_event_forwarding_always_enabled() { always_input_forwarding_list->add_plugin(this); } +void EditorPlugin::set_force_draw_over_forwarding_enabled() { + force_draw_over_forwarding_enabled = true; + EditorPluginList *always_draw_over_forwarding_list = EditorNode::get_singleton()->get_editor_plugins_force_over(); + always_draw_over_forwarding_list->add_plugin(this); +} + void EditorPlugin::notify_scene_changed(const Node *scn_root) { if (scn_root == NULL) return; emit_signal("scene_changed", scn_root); @@ -410,15 +416,38 @@ bool EditorPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; } -void EditorPlugin::forward_draw_over_canvas(Control *p_canvas) { +void EditorPlugin::forward_draw_over_viewport(Control *p_overlay) { - if (get_script_instance() && get_script_instance()->has_method("forward_draw_over_canvas")) { - get_script_instance()->call("forward_draw_over_canvas", p_canvas); + if (get_script_instance() && get_script_instance()->has_method("forward_draw_over_viewport")) { + get_script_instance()->call("forward_draw_over_viewport", p_overlay); } } -void EditorPlugin::update_canvas() { - CanvasItemEditor::get_singleton()->get_viewport_control()->update(); +void EditorPlugin::forward_force_draw_over_viewport(Control *p_overlay) { + + if (get_script_instance() && get_script_instance()->has_method("forward_force_draw_over_viewport")) { + get_script_instance()->call("forward_force_draw_over_viewport", p_overlay); + } +} + +// Updates the overlays of the 2D viewport or, if in 3D mode, of every 3D viewport. +int EditorPlugin::update_overlays() const { + + if (SpatialEditor::get_singleton()->is_visible()) { + int count = 0; + for (int i = 0; i < SpatialEditor::VIEWPORTS_COUNT; i++) { + SpatialEditorViewport *vp = SpatialEditor::get_singleton()->get_editor_viewport(i); + if (vp->is_visible()) { + vp->update_surface(); + count++; + } + } + return count; + } else { + // This will update the normal viewport itself as well + CanvasItemEditor::get_singleton()->get_viewport_control()->update(); + return 1; + } } bool EditorPlugin::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { @@ -590,7 +619,7 @@ void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("add_custom_type", "type", "base", "script", "icon"), &EditorPlugin::add_custom_type); ClassDB::bind_method(D_METHOD("remove_custom_type", "type"), &EditorPlugin::remove_custom_type); - ClassDB::bind_method(D_METHOD("update_canvas"), &EditorPlugin::update_canvas); + ClassDB::bind_method(D_METHOD("update_overlays"), &EditorPlugin::update_overlays); ClassDB::bind_method(D_METHOD("make_bottom_panel_item_visible", "item"), &EditorPlugin::make_bottom_panel_item_visible); ClassDB::bind_method(D_METHOD("hide_bottom_panel"), &EditorPlugin::hide_bottom_panel); @@ -602,11 +631,13 @@ void EditorPlugin::_bind_methods() { 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("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); ClassDB::bind_method(D_METHOD("get_editor_interface"), &EditorPlugin::get_editor_interface); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_canvas_gui_input", PropertyInfo(Variant::TRANSFORM2D, "canvas_xform"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); - ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_draw_over_canvas", PropertyInfo(Variant::TRANSFORM2D, "canvas_xform"), PropertyInfo(Variant::OBJECT, "canvas", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_force_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_spatial_gui_input", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); MethodInfo gizmo = MethodInfo(Variant::OBJECT, "create_spatial_gizmo", PropertyInfo(Variant::OBJECT, "for_spatial", PROPERTY_HINT_RESOURCE_TYPE, "Spatial")); gizmo.return_val.hint = PROPERTY_HINT_RESOURCE_TYPE; @@ -653,6 +684,7 @@ void EditorPlugin::_bind_methods() { EditorPlugin::EditorPlugin() { undo_redo = NULL; input_event_forwarding_always_enabled = false; + force_draw_over_forwarding_enabled = false; last_main_screen_name = ""; } diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 1d68eee117..11f4378667 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -102,6 +102,7 @@ class EditorPlugin : public Node { UndoRedo *_get_undo_redo() { return undo_redo; } bool input_event_forwarding_always_enabled; + bool force_draw_over_forwarding_enabled; String last_main_screen_name; @@ -151,13 +152,17 @@ public: void set_input_event_forwarding_always_enabled(); bool is_input_event_forwarding_always_enabled() { return input_event_forwarding_always_enabled; } + void set_force_draw_over_forwarding_enabled(); + bool is_force_draw_over_forwarding_enabled() { return force_draw_over_forwarding_enabled; } + void notify_main_screen_changed(const String &screen_name); void notify_scene_changed(const Node *scn_root); void notify_scene_closed(const String &scene_filepath); virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial); virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event); - virtual void forward_draw_over_canvas(Control *p_canvas); + virtual void forward_draw_over_viewport(Control *p_overlay); + virtual void forward_force_draw_over_viewport(Control *p_overlay); virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event); virtual String get_name() const; virtual bool has_main_screen() const; @@ -178,7 +183,7 @@ public: EditorInterface *get_editor_interface(); - void update_canvas(); + int update_overlays() const; void queue_save_layout() const; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 818f1b0218..52672fff72 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -462,6 +462,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("editors/animation/autorename_animation_tracks", true); _initial_set("editors/animation/confirm_insert_track", true); + _initial_set("editors/animation/onion_layers_past_color", Color(1, 0, 0)); + _initial_set("editors/animation/onion_layers_future_color", Color(0, 1, 0)); _initial_set("docks/property_editor/texture_preview_width", 48); _initial_set("docks/property_editor/auto_refresh_interval", 0.3); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 2ddfea00e3..a5445ca153 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1565,6 +1565,7 @@ void FileSystemDock::_bind_methods() { FileSystemDock::FileSystemDock(EditorNode *p_editor) { + set_name("FileSystem"); editor = p_editor; path = "res://"; diff --git a/editor/icons/icon_editor_handle_add.svg b/editor/icons/icon_editor_handle_add.svg new file mode 100644 index 0000000000..be61cd53f9 --- /dev/null +++ b/editor/icons/icon_editor_handle_add.svg @@ -0,0 +1,5 @@ +<svg width="10" height="10" version="1.1" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg"> + <circle cx="5" cy="5" r="5" fill-opacity=".29412"/> + <circle cx="5" cy="5" r="4" fill="#474747"/> + <path d="m4 2v2h-2v2h2v2h2v-2h2v-2h-2v-2z" fill="#84ffb1"/> +</svg> diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 84d55b4d14..8a16f125b7 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -403,6 +403,7 @@ void ImportDock::initialize_import_options() const { ImportDock::ImportDock() { + set_name("Import"); imported = memnew(Label); imported->add_style_override("normal", EditorNode::get_singleton()->get_gui_base()->get_stylebox("normal", "LineEdit")); add_child(imported); diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp index 20392a67a7..366230be51 100644 --- a/editor/node_dock.cpp +++ b/editor/node_dock.cpp @@ -93,9 +93,10 @@ void NodeDock::set_node(Node *p_node) { } NodeDock::NodeDock() { + singleton = this; - set_name(TTR("Node")); + set_name("Node"); mode_hb = memnew(HBoxContainer); add_child(mode_hb); mode_hb->hide(); diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 736e176ab8..cabdfa761d 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -490,15 +490,14 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) return false; } -void AbstractPolygon2DEditor::forward_draw_over_canvas(Control *p_canvas) { +void AbstractPolygon2DEditor::forward_draw_over_viewport(Control *p_overlay) { if (!_get_node()) return; Control *vpc = canvas_item_editor->get_viewport_control(); Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform(); - Ref<Texture> default_handle = get_icon("EditorHandle", "EditorIcons"); - Ref<Texture> selected_handle = get_icon("EditorHandleSelected", "EditorIcons"); + const Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons"); const Vertex active_point = get_active_point(); const int n_polygons = _get_polygon_count(); @@ -572,8 +571,8 @@ void AbstractPolygon2DEditor::forward_draw_over_canvas(Control *p_canvas) { const Vector2 p = (vertex == edited_point) ? edited_point.pos : (points[i] + offset); const Vector2 point = xform.xform(p); - Ref<Texture> handle = vertex == active_point ? selected_handle : default_handle; - vpc->draw_texture(handle, point - handle->get_size() * 0.5); + const Color modulate = vertex == active_point ? Color(0.5, 1, 2) : Color(1, 1, 1); + vpc->draw_texture(handle, point - handle->get_size() * 0.5, modulate); } } diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h index 915fe0803e..545eff6ef4 100644 --- a/editor/plugins/abstract_polygon_2d_editor.h +++ b/editor/plugins/abstract_polygon_2d_editor.h @@ -136,7 +136,7 @@ protected: public: bool forward_gui_input(const Ref<InputEvent> &p_event); - void forward_draw_over_canvas(Control *p_canvas); + void forward_draw_over_viewport(Control *p_overlay); void edit(Node *p_polygon); AbstractPolygon2DEditor(EditorNode *p_editor, bool p_wip_destructive = true); @@ -152,7 +152,7 @@ class AbstractPolygon2DEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return polygon_editor->forward_gui_input(p_event); } - virtual void forward_draw_over_canvas(Control *p_canvas) { polygon_editor->forward_draw_over_canvas(p_canvas); } + virtual void forward_draw_over_viewport(Control *p_overlay) { polygon_editor->forward_draw_over_viewport(p_overlay); } bool has_main_screen() const { return false; } virtual String get_name() const { return klass; } diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 2b9c625aa4..019e32f847 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -36,6 +36,12 @@ #include "os/keyboard.h" #include "project_settings.h" +// For onion skinning +#include "editor/plugins/canvas_item_editor_plugin.h" +#include "editor/plugins/spatial_editor_plugin.h" +#include "scene/main/viewport.h" +#include "servers/visual_server.h" + void AnimationPlayerEditor::_node_removed(Node *p_node) { if (player && player == p_node) { @@ -98,6 +104,8 @@ void AnimationPlayerEditor::_notification(int p_what) { tool_anim->get_popup()->connect("id_pressed", this, "_animation_tool_menu"); + onion_skinning->get_popup()->connect("id_pressed", this, "_onion_skinning_menu"); + blend_editor.next->connect("item_selected", this, "_blend_editor_next_changed"); get_tree()->connect("node_removed", this, "_node_removed"); @@ -132,6 +140,7 @@ void AnimationPlayerEditor::_notification(int p_what) { resource_edit_anim->set_icon(get_icon("EditResource", "EditorIcons")); pin->set_icon(get_icon("Pin", "EditorIcons")); tool_anim->set_icon(get_icon("Tools", "EditorIcons")); + onion_skinning->set_icon(get_icon("Onion", "EditorIcons")); } break; } @@ -809,6 +818,7 @@ void AnimationPlayerEditor::_update_player() { resource_edit_anim->set_disabled(animlist.size() == 0); save_anim->set_disabled(animlist.size() == 0); tool_anim->set_disabled(player == NULL); + onion_skinning->set_disabled(player == NULL); int active_idx = -1; for (List<StringName>::Element *E = animlist.front(); E; E = E->next()) { @@ -855,6 +865,9 @@ void AnimationPlayerEditor::_update_player() { void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { + if (onion.enabled) + _start_onion_skinning(); + if (player && pin->is_pressed()) return; //ignore, pinned player = p_player; @@ -869,6 +882,55 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { } } +void AnimationPlayerEditor::forward_force_draw_over_viewport(Control *p_overlay) { + + if (!onion.can_overlay) + return; + + // Can happen on viewport resize, at least + if (!_are_onion_layers_valid()) + return; + + RID ci = p_overlay->get_canvas_item(); + Rect2 src_rect = p_overlay->get_global_rect(); + // Re-flip since captures are already flipped + src_rect.position.y = onion.capture_size.y - (src_rect.position.y + src_rect.size.y); + src_rect.size.y *= -1; + + Rect2 dst_rect = Rect2(Point2(), p_overlay->get_size()); + + float alpha_step = 1.0 / (onion.steps + 1); + + int cidx = 0; + if (onion.past) { + float alpha = 0; + do { + alpha += alpha_step; + + if (onion.captures_valid[cidx]) { + VS::get_singleton()->canvas_item_add_texture_rect_region( + ci, dst_rect, VS::get_singleton()->viewport_get_texture(onion.captures[cidx]), src_rect, Color(1, 1, 1, alpha)); + } + + cidx++; + } while (cidx < onion.steps); + } + if (onion.future) { + float alpha = 1; + int base_cidx = cidx; + do { + alpha -= alpha_step; + + if (onion.captures_valid[cidx]) { + VS::get_singleton()->canvas_item_add_texture_rect_region( + ci, dst_rect, VS::get_singleton()->viewport_get_texture(onion.captures[cidx]), src_rect, Color(1, 1, 1, alpha)); + } + + cidx++; + } while (cidx < base_cidx + onion.steps); // In case there's the present capture at the end, skip it + } +} + void AnimationPlayerEditor::_animation_duplicate() { if (!animation->get_item_count()) @@ -1100,6 +1162,70 @@ void AnimationPlayerEditor::_animation_save_menu(int p_option) { } } +void AnimationPlayerEditor::_onion_skinning_menu(int p_option) { + + PopupMenu *menu = onion_skinning->get_popup(); + int idx = menu->get_item_index(p_option); + + switch (p_option) { + + case ONION_SKINNING_ENABLE: { + + onion.enabled = !onion.enabled; + menu->set_item_checked(idx, onion.enabled); + + if (onion.enabled) + _start_onion_skinning(); + else + _stop_onion_skinning(); + + } break; + + case ONION_SKINNING_PAST: { + + // Ensure at least one of past/future is checjed + onion.past = onion.future ? !onion.past : true; + menu->set_item_checked(idx, onion.past); + } break; + + case ONION_SKINNING_FUTURE: { + + // Ensure at least one of past/future is checjed + onion.future = onion.past ? !onion.future : true; + menu->set_item_checked(idx, onion.future); + } break; + + case ONION_SKINNING_1_STEP: // Fall-through + case ONION_SKINNING_2_STEPS: + case ONION_SKINNING_3_STEPS: { + + onion.steps = (p_option - ONION_SKINNING_1_STEP) + 1; + int one_frame_idx = menu->get_item_index(ONION_SKINNING_1_STEP); + for (int i = 0; i <= ONION_SKINNING_LAST_STEPS_OPTION - ONION_SKINNING_1_STEP; i++) { + menu->set_item_checked(one_frame_idx + i, onion.steps == i + 1); + } + } break; + + case ONION_SKINNING_DIFFERENCES_ONLY: { + + onion.differences_only = !onion.differences_only; + menu->set_item_checked(idx, onion.differences_only); + } break; + + case ONION_SKINNING_FORCE_WHITE_MODULATE: { + + onion.force_white_modulate = !onion.force_white_modulate; + menu->set_item_checked(idx, onion.force_white_modulate); + } break; + + case ONION_SKINNING_INCLUDE_GIZMOS: { + + onion.include_gizmos = !onion.include_gizmos; + menu->set_item_checked(idx, onion.include_gizmos); + } break; + } +} + void AnimationPlayerEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) { Ref<InputEventKey> k = p_ev; @@ -1126,6 +1252,237 @@ void AnimationPlayerEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) { } } +void AnimationPlayerEditor::_editor_visibility_changed() { + + if (is_visible()) { + _start_onion_skinning(); + } +} + +bool AnimationPlayerEditor::_are_onion_layers_valid() { + + ERR_FAIL_COND_V(!onion.past && !onion.future, false); + + Point2 capture_size = get_tree()->get_root()->get_size(); + return onion.captures.size() == onion.get_needed_capture_count() && onion.capture_size == capture_size; +} + +void AnimationPlayerEditor::_allocate_onion_layers() { + + _free_onion_layers(); + + int captures = onion.get_needed_capture_count(); + Point2 capture_size = get_tree()->get_root()->get_size(); + + onion.captures.resize(captures); + onion.captures_valid.resize(captures); + + for (int i = 0; i < captures; i++) { + bool is_present = onion.differences_only && i == captures - 1; + + // Each capture is a viewport with a canvas item attached that renders a full-size rect with the contents of the main viewport + onion.captures[i] = VS::get_singleton()->viewport_create(); + VS::get_singleton()->viewport_set_usage(onion.captures[i], VS::VIEWPORT_USAGE_2D); + VS::get_singleton()->viewport_set_size(onion.captures[i], capture_size.width, capture_size.height); + VS::get_singleton()->viewport_set_update_mode(onion.captures[i], VS::VIEWPORT_UPDATE_ALWAYS); + VS::get_singleton()->viewport_set_transparent_background(onion.captures[i], !is_present); + VS::get_singleton()->viewport_set_vflip(onion.captures[i], true); + VS::get_singleton()->viewport_attach_canvas(onion.captures[i], onion.capture.canvas); + } + + // Reset the capture canvas item to the current root viewport texture (defensive) + VS::get_singleton()->canvas_item_clear(onion.capture.canvas_item); + VS::get_singleton()->canvas_item_add_texture_rect(onion.capture.canvas_item, Rect2(Point2(), capture_size), get_tree()->get_root()->get_texture()->get_rid()); + + onion.capture_size = capture_size; +} + +void AnimationPlayerEditor::_free_onion_layers() { + + for (int i = 0; i < onion.captures.size(); i++) { + if (onion.captures[i].is_valid()) { + VS::get_singleton()->free(onion.captures[i]); + } + } + onion.captures.clear(); + onion.captures_valid.clear(); +} + +void AnimationPlayerEditor::_prepare_onion_layers_1() { + + // This would be called per viewport and we want to act once only + int64_t frame = get_tree()->get_frame(); + if (frame == onion.last_frame) + return; + + if (!onion.enabled || !is_processing() || !is_visible() || !get_player()) { + _stop_onion_skinning(); + return; + } + + onion.last_frame = frame; + + // Refresh viewports with no onion layers overlaid + onion.can_overlay = false; + plugin->update_overlays(); + + if (player->is_playing()) + return; + + // And go to next step afterwards + call_deferred("_prepare_onion_layers_2"); +} + +void AnimationPlayerEditor::_prepare_onion_layers_2() { + + Ref<Animation> anim = player->get_animation(player->get_current_animation()); + if (!anim.is_valid()) + return; + + if (!_are_onion_layers_valid()) + _allocate_onion_layers(); + + // Hide superfluous elements that would make the overlay unnecessary cluttered + Dictionary canvas_edit_state; + Dictionary spatial_edit_state; + if (SpatialEditor::get_singleton()->is_visible()) { + // 3D + spatial_edit_state = SpatialEditor::get_singleton()->get_state(); + Dictionary new_state = spatial_edit_state.copy(); + new_state["show_grid"] = false; + new_state["show_origin"] = false; + Array orig_vp = spatial_edit_state["viewports"]; + Array vp; + vp.resize(4); + for (int i = 0; i < vp.size(); i++) { + Dictionary d = ((Dictionary)orig_vp[i]).copy(); + d["use_environment"] = false; + d["doppler"] = false; + d["gizmos"] = onion.include_gizmos ? d["gizmos"] : Variant(false); + d["information"] = false; + vp[i] = d; + } + new_state["viewports"] = vp; + // TODO: Save/restore only affected entries + SpatialEditor::get_singleton()->set_state(new_state); + } else { // CanvasItemEditor + // 2D + canvas_edit_state = CanvasItemEditor::get_singleton()->get_state(); + Dictionary new_state = canvas_edit_state.copy(); + new_state["show_grid"] = false; + new_state["show_rulers"] = false; + new_state["show_guides"] = false; + new_state["show_helpers"] = false; + // TODO: Save/restore only affected entries + CanvasItemEditor::get_singleton()->set_state(new_state); + } + + // Tweak the root viewport to ensure it's rendered before our target + RID root_vp = get_tree()->get_root()->get_viewport_rid(); + Rect2 root_vp_screen_rect = get_tree()->get_root()->get_attach_to_screen_rect(); + VS::get_singleton()->viewport_attach_to_screen(root_vp, Rect2()); + VS::get_singleton()->viewport_set_update_mode(root_vp, VS::VIEWPORT_UPDATE_ALWAYS); + + RID present_rid; + if (onion.differences_only) { + // Capture present scene as it is + VS::get_singleton()->canvas_item_set_material(onion.capture.canvas_item, RID()); + present_rid = onion.captures[onion.captures.size() - 1]; + VS::get_singleton()->viewport_set_active(present_rid, true); + VS::get_singleton()->viewport_set_parent_viewport(root_vp, present_rid); + VS::get_singleton()->draw(false); + VS::get_singleton()->viewport_set_active(present_rid, false); + } + + // Backup current animation state + AnimatedValuesBackup values_backup = player->backup_animated_values(); + float cpos = player->get_current_animation_position(); + + // Render every past/future step with the capture shader + + VS::get_singleton()->canvas_item_set_material(onion.capture.canvas_item, onion.capture.material->get_rid()); + onion.capture.material->set_shader_param("bkg_color", GLOBAL_GET("rendering/environment/default_clear_color")); + onion.capture.material->set_shader_param("differences_only", onion.differences_only); + onion.capture.material->set_shader_param("present", onion.differences_only ? VS::get_singleton()->viewport_get_texture(present_rid) : RID()); + + int step_off_a = onion.past ? -onion.steps : 0; + int step_off_b = onion.future ? onion.steps : 0; + int cidx = 0; + onion.capture.material->set_shader_param("dir_color", onion.force_white_modulate ? Color(1, 1, 1) : Color(EDITOR_GET("editors/animation/onion_layers_past_color"))); + for (int step_off = step_off_a; step_off <= step_off_b; step_off++) { + + if (step_off == 0) { + // Skip present step and switch to the color of future + if (!onion.force_white_modulate) + onion.capture.material->set_shader_param("dir_color", EDITOR_GET("editors/animation/onion_layers_future_color")); + continue; + } + + float pos = cpos + step_off * anim->get_step(); + + bool valid = anim->has_loop() || pos >= 0 && pos <= anim->get_length(); + onion.captures_valid[cidx] = valid; + if (valid) { + player->seek(pos, true); + get_tree()->flush_transform_notifications(); // Needed for transforms of Spatials + values_backup.update_skeletons(); // Needed for Skeletons + + VS::get_singleton()->viewport_set_active(onion.captures[cidx], true); + VS::get_singleton()->viewport_set_parent_viewport(root_vp, onion.captures[cidx]); + VS::get_singleton()->draw(false); + VS::get_singleton()->viewport_set_active(onion.captures[cidx], false); + } + + cidx++; + } + + // Restore root viewport + VS::get_singleton()->viewport_set_parent_viewport(root_vp, RID()); + VS::get_singleton()->viewport_attach_to_screen(root_vp, root_vp_screen_rect); + VS::get_singleton()->viewport_set_update_mode(root_vp, VS::VIEWPORT_UPDATE_WHEN_VISIBLE); + + // Restore animation state + // (Seeking with update=true wouldn't do the trick because the current value of the properties + // may not match their value for the current point in the animation) + player->seek(cpos, false); + player->restore_animated_values(values_backup); + + // Restor state of main editors + if (SpatialEditor::get_singleton()->is_visible()) { + // 3D + SpatialEditor::get_singleton()->set_state(spatial_edit_state); + } else { // CanvasItemEditor + // 2D + CanvasItemEditor::get_singleton()->set_state(canvas_edit_state); + } + + // Update viewports with skin layers overlaid for the actual engine loop render + onion.can_overlay = true; + plugin->update_overlays(); +} + +void AnimationPlayerEditor::_start_onion_skinning() { + + // FIXME: Using "idle_frame" makes onion layers update one frame behing the current + if (!get_tree()->is_connected("idle_frame", this, "call_deferred")) { + get_tree()->connect("idle_frame", this, "call_deferred", varray("_prepare_onion_layers_1")); + } +} + +void AnimationPlayerEditor::_stop_onion_skinning() { + + if (get_tree()->is_connected("idle_frame", this, "call_deferred")) { + + get_tree()->disconnect("idle_frame", this, "call_deferred"); + + _free_onion_layers(); + + // Clean up the overlay + onion.can_overlay = false; + plugin->update_overlays(); + } +} + void AnimationPlayerEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &AnimationPlayerEditor::_gui_input); @@ -1165,6 +1522,10 @@ void AnimationPlayerEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &AnimationPlayerEditor::_unhandled_key_input); ClassDB::bind_method(D_METHOD("_animation_tool_menu"), &AnimationPlayerEditor::_animation_tool_menu); ClassDB::bind_method(D_METHOD("_animation_save_menu"), &AnimationPlayerEditor::_animation_save_menu); + ClassDB::bind_method(D_METHOD("_onion_skinning_menu"), &AnimationPlayerEditor::_onion_skinning_menu); + ClassDB::bind_method(D_METHOD("_editor_visibility_changed"), &AnimationPlayerEditor::_editor_visibility_changed); + ClassDB::bind_method(D_METHOD("_prepare_onion_layers_1"), &AnimationPlayerEditor::_prepare_onion_layers_1); + ClassDB::bind_method(D_METHOD("_prepare_onion_layers_2"), &AnimationPlayerEditor::_prepare_onion_layers_2); } AnimationPlayerEditor *AnimationPlayerEditor::singleton = NULL; @@ -1173,8 +1534,10 @@ AnimationPlayer *AnimationPlayerEditor::get_player() const { return player; } -AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { + +AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlayerEditorPlugin *p_plugin) { editor = p_editor; + plugin = p_plugin; singleton = this; updating = false; @@ -1301,6 +1664,29 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { //tool_anim->get_popup()->add_item("Edit Anim Resource",TOOL_PASTE_ANIM); hb->add_child(tool_anim); + onion_skinning = memnew(MenuButton); + //onion_skinning->set_flat(false); + onion_skinning->set_tooltip(TTR("Onion Skinning")); + onion_skinning->get_popup()->add_check_shortcut(ED_SHORTCUT("animation_player_editor/onion_skinning", TTR("Enable Onion Skinning")), ONION_SKINNING_ENABLE); + onion_skinning->get_popup()->add_separator(); + onion_skinning->get_popup()->add_item(TTR("Directions"), -1); + onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true); + onion_skinning->get_popup()->add_check_item(TTR("Past"), ONION_SKINNING_PAST); + onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true); + onion_skinning->get_popup()->add_check_item(TTR("Future"), ONION_SKINNING_FUTURE); + onion_skinning->get_popup()->add_separator(); + onion_skinning->get_popup()->add_item(TTR("Depth"), -1); + onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true); + onion_skinning->get_popup()->add_check_item(TTR("1 step"), ONION_SKINNING_1_STEP); + onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true); + onion_skinning->get_popup()->add_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS); + onion_skinning->get_popup()->add_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS); + onion_skinning->get_popup()->add_separator(); + onion_skinning->get_popup()->add_check_item(TTR("Differences Only"), ONION_SKINNING_DIFFERENCES_ONLY); + onion_skinning->get_popup()->add_check_item(TTR("Force White Modulate"), ONION_SKINNING_FORCE_WHITE_MODULATE); + onion_skinning->get_popup()->add_check_item(TTR("Include Gizmos (3D)"), ONION_SKINNING_INCLUDE_GIZMOS); + hb->add_child(onion_skinning); + pin = memnew(ToolButton); pin->set_toggle_mode(true); hb->add_child(pin); @@ -1387,6 +1773,68 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { key_editor->connect("animation_step_changed", this, "_animation_key_editor_anim_step_changed"); _update_player(); + + // Onion skinning + + key_editor->connect("visibility_changed", this, "_editor_visibility_changed"); + + onion.enabled = false; + onion.past = true; + onion.future = false; + onion.steps = 1; + onion.differences_only = false; + onion.force_white_modulate = false; + onion.include_gizmos = false; + + onion.last_frame = 0; + onion.can_overlay = false; + onion.capture_size = Size2(); + onion.capture.canvas = VS::get_singleton()->canvas_create(); + onion.capture.canvas_item = VS::get_singleton()->canvas_item_create(); + VS::get_singleton()->canvas_item_set_parent(onion.capture.canvas_item, onion.capture.canvas); + + onion.capture.material = Ref<ShaderMaterial>(memnew(ShaderMaterial)); + + onion.capture.shader = Ref<Shader>(memnew(Shader)); + onion.capture.shader->set_code(" \ + shader_type canvas_item; \ + \ + uniform vec4 bkg_color; \ + uniform vec4 dir_color; \ + uniform bool differences_only; \ + uniform sampler2D present; \ + \ + float zero_if_equal(vec4 a, vec4 b) { \ + return smoothstep(0.0, 0.005, length(a.rgb - b.rgb) / sqrt(3.0)); \ + } \ + \ + void fragment() { \ + vec4 capture_samp = texture(TEXTURE, UV); \ + vec4 present_samp = texture(present, UV); \ + float bkg_mask = zero_if_equal(capture_samp, bkg_color); \ + float diff_mask = 1.0 - zero_if_equal(present_samp, bkg_color); \ + diff_mask = min(1.0, diff_mask + float(!differences_only)); \ + COLOR = vec4(capture_samp.rgb * dir_color.rgb, bkg_mask * diff_mask); \ + } \ + "); + VS::get_singleton()->material_set_shader(onion.capture.material->get_rid(), onion.capture.shader->get_rid()); +} + +AnimationPlayerEditor::~AnimationPlayerEditor() { + + _free_onion_layers(); + VS::get_singleton()->free(onion.capture.canvas); + VS::get_singleton()->free(onion.capture.canvas_item); +} + +void AnimationPlayerEditorPlugin::_notification(int p_what) { + + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + + set_force_draw_over_forwarding_enabled(); + } break; + } } void AnimationPlayerEditorPlugin::edit(Object *p_object) { @@ -1420,7 +1868,7 @@ void AnimationPlayerEditorPlugin::make_visible(bool p_visible) { AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin(EditorNode *p_node) { editor = p_node; - anim_editor = memnew(AnimationPlayerEditor(editor)); + anim_editor = memnew(AnimationPlayerEditor(editor, this)); anim_editor->set_undo_redo(editor->get_undo_redo()); editor->add_bottom_panel_item(TTR("Animation"), anim_editor); diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index cea6b05ed4..1a1e92d7b7 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -42,11 +42,13 @@ @author Juan Linietsky <reduzio@gmail.com> */ class AnimationKeyEditor; +class AnimationPlayerEditorPlugin; class AnimationPlayerEditor : public VBoxContainer { GDCLASS(AnimationPlayerEditor, VBoxContainer); EditorNode *editor; + AnimationPlayerEditorPlugin *plugin; AnimationPlayer *player; enum { @@ -56,6 +58,19 @@ class AnimationPlayerEditor : public VBoxContainer { }; enum { + ONION_SKINNING_ENABLE, + ONION_SKINNING_PAST, + ONION_SKINNING_FUTURE, + ONION_SKINNING_1_STEP, + ONION_SKINNING_2_STEPS, + ONION_SKINNING_3_STEPS, + ONION_SKINNING_LAST_STEPS_OPTION = ONION_SKINNING_3_STEPS, + ONION_SKINNING_DIFFERENCES_ONLY, + ONION_SKINNING_FORCE_WHITE_MODULATE, + ONION_SKINNING_INCLUDE_GIZMOS, + }; + + enum { ANIM_SAVE, ANIM_SAVE_AS }; @@ -84,6 +99,7 @@ class AnimationPlayerEditor : public VBoxContainer { Button *blend_anim; Button *remove_anim; MenuButton *tool_anim; + MenuButton *onion_skinning; ToolButton *pin; SpinBox *frame; LineEdit *scale; @@ -115,6 +131,36 @@ class AnimationPlayerEditor : public VBoxContainer { AnimationKeyEditor *key_editor; + // Onion skinning + struct { + // Settings + bool enabled; + bool past; + bool future; + int steps; + bool differences_only; + bool force_white_modulate; + bool include_gizmos; + + int get_needed_capture_count() const { + // 'Differences only' needs a capture of the present + return (past && future ? 2 * steps : steps) + (differences_only ? 1 : 0); + } + + // Rendering + int64_t last_frame; + int can_overlay; + Size2 capture_size; + Vector<RID> captures; + Vector<bool> captures_valid; + struct { + RID canvas; + RID canvas_item; + Ref<ShaderMaterial> material; + Ref<Shader> shader; + } capture; + } onion; + void _select_anim_by_name(const String &p_anim); void _play_pressed(); void _play_from_pressed(); @@ -161,8 +207,19 @@ class AnimationPlayerEditor : public VBoxContainer { void _unhandled_key_input(const Ref<InputEvent> &p_ev); void _animation_tool_menu(int p_option); void _animation_save_menu(int p_option); + void _onion_skinning_menu(int p_option); + + void _editor_visibility_changed(); + bool _are_onion_layers_valid(); + void _allocate_onion_layers(); + void _free_onion_layers(); + void _prepare_onion_layers_1(); + void _prepare_onion_layers_2(); + void _start_onion_skinning(); + void _stop_onion_skinning(); AnimationPlayerEditor(); + ~AnimationPlayerEditor(); protected: void _notification(int p_what); @@ -182,7 +239,9 @@ public: void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } void edit(AnimationPlayer *p_player); - AnimationPlayerEditor(EditorNode *p_editor); + void forward_force_draw_over_viewport(Control *p_overlay); + + AnimationPlayerEditor(EditorNode *p_editor, AnimationPlayerEditorPlugin *p_plugin); }; class AnimationPlayerEditorPlugin : public EditorPlugin { @@ -192,6 +251,9 @@ class AnimationPlayerEditorPlugin : public EditorPlugin { AnimationPlayerEditor *anim_editor; EditorNode *editor; +protected: + void _notification(int p_what); + public: virtual Dictionary get_state() const { return anim_editor->get_state(); } virtual void set_state(const Dictionary &p_state) { anim_editor->set_state(p_state); } @@ -202,6 +264,8 @@ public: virtual bool handles(Object *p_object) const; virtual void make_visible(bool p_visible); + virtual void forward_force_draw_over_viewport(Control *p_overlay) { anim_editor->forward_force_draw_over_viewport(p_overlay); } + AnimationPlayerEditorPlugin(EditorNode *p_node); ~AnimationPlayerEditorPlugin(); }; diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index b63352389e..eee54f3cd2 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -1268,6 +1268,10 @@ void EditorAssetLibrary::_install_external_asset(String p_zip_path, String p_tit emit_signal("install_asset", p_zip_path, p_title); } +void EditorAssetLibrary::disable_community_support() { + support->get_popup()->set_item_checked(SUPPORT_COMMUNITY, false); +} + void EditorAssetLibrary::_bind_methods() { ClassDB::bind_method("_http_request_completed", &EditorAssetLibrary::_http_request_completed); diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index 35977f3949..90d597e70a 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -301,6 +301,8 @@ protected: void _notification(int p_what); public: + void disable_community_support(); + EditorAssetLibrary(bool p_templates_only = false); }; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 3940dd9044..d18e97fe83 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -2955,8 +2955,13 @@ void CanvasItemEditor::_draw_viewport() { EditorPluginList *over_plugin_list = editor->get_editor_plugins_over(); if (!over_plugin_list->empty()) { - over_plugin_list->forward_draw_over_canvas(viewport); + over_plugin_list->forward_draw_over_viewport(viewport); } + EditorPluginList *force_over_plugin_list = editor->get_editor_plugins_force_over(); + if (!force_over_plugin_list->empty()) { + force_over_plugin_list->forward_force_draw_over_viewport(viewport); + } + _draw_bones(); } diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index 005de096cd..029e3a558d 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -414,7 +414,7 @@ void CollisionShape2DEditor::_get_current_shape_type() { canvas_item_editor->get_viewport_control()->update(); } -void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) { +void CollisionShape2DEditor::forward_draw_over_viewport(Control *p_overlay) { if (!node) { return; @@ -448,8 +448,8 @@ void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) { handles[0] = Point2(radius, -height); handles[1] = Point2(0, -(height + radius)); - p_canvas->draw_texture(h, gt.xform(handles[0]) - size); - p_canvas->draw_texture(h, gt.xform(handles[1]) - size); + p_overlay->draw_texture(h, gt.xform(handles[0]) - size); + p_overlay->draw_texture(h, gt.xform(handles[1]) - size); } break; @@ -459,7 +459,7 @@ void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) { handles.resize(1); handles[0] = Point2(shape->get_radius(), 0); - p_canvas->draw_texture(h, gt.xform(handles[0]) - size); + p_overlay->draw_texture(h, gt.xform(handles[0]) - size); } break; @@ -478,8 +478,8 @@ void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) { handles[0] = shape->get_normal() * shape->get_d(); handles[1] = shape->get_normal() * (shape->get_d() + 30.0); - p_canvas->draw_texture(h, gt.xform(handles[0]) - size); - p_canvas->draw_texture(h, gt.xform(handles[1]) - size); + p_overlay->draw_texture(h, gt.xform(handles[0]) - size); + p_overlay->draw_texture(h, gt.xform(handles[1]) - size); } break; @@ -489,7 +489,7 @@ void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) { handles.resize(1); handles[0] = Point2(0, shape->get_length()); - p_canvas->draw_texture(h, gt.xform(handles[0]) - size); + p_overlay->draw_texture(h, gt.xform(handles[0]) - size); } break; @@ -501,8 +501,8 @@ void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) { handles[0] = Point2(ext.x, 0); handles[1] = Point2(0, -ext.y); - p_canvas->draw_texture(h, gt.xform(handles[0]) - size); - p_canvas->draw_texture(h, gt.xform(handles[1]) - size); + p_overlay->draw_texture(h, gt.xform(handles[0]) - size); + p_overlay->draw_texture(h, gt.xform(handles[1]) - size); } break; @@ -513,8 +513,8 @@ void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) { handles[0] = shape->get_a(); handles[1] = shape->get_b(); - p_canvas->draw_texture(h, gt.xform(handles[0]) - size); - p_canvas->draw_texture(h, gt.xform(handles[1]) - size); + p_overlay->draw_texture(h, gt.xform(handles[0]) - size); + p_overlay->draw_texture(h, gt.xform(handles[1]) - size); } break; } diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h index d4fbe87fb3..1e930ef371 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.h +++ b/editor/plugins/collision_shape_2d_editor_plugin.h @@ -74,7 +74,7 @@ protected: public: bool forward_canvas_gui_input(const Ref<InputEvent> &p_event); - void forward_draw_over_canvas(Control *p_canvas); + void forward_draw_over_viewport(Control *p_overlay); void edit(Node *p_node); CollisionShape2DEditor(EditorNode *p_editor); @@ -88,7 +88,7 @@ class CollisionShape2DEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return collision_shape_2d_editor->forward_canvas_gui_input(p_event); } - virtual void forward_draw_over_canvas(Control *p_canvas) { return collision_shape_2d_editor->forward_draw_over_canvas(p_canvas); } + virtual void forward_draw_over_viewport(Control *p_overlay) { return collision_shape_2d_editor->forward_draw_over_viewport(p_overlay); } virtual String get_name() const { return "CollisionShape2D"; } bool has_main_screen() const { return false; } diff --git a/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp index 485657d2c9..3febc99239 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.cpp +++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp @@ -318,7 +318,7 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return false; } -void LightOccluder2DEditor::forward_draw_over_canvas(Control *p_canvas) { +void LightOccluder2DEditor::forward_draw_over_viewport(Control *p_overlay) { if (!node || !node->get_occluder_polygon().is_valid()) return; diff --git a/editor/plugins/light_occluder_2d_editor_plugin.h b/editor/plugins/light_occluder_2d_editor_plugin.h index 068832d8ed..dc3ff74052 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.h +++ b/editor/plugins/light_occluder_2d_editor_plugin.h @@ -82,7 +82,7 @@ protected: public: Vector2 snap_point(const Vector2 &p_point) const; - void forward_draw_over_canvas(Control *p_canvas); + void forward_draw_over_viewport(Control *p_overlay); bool forward_gui_input(const Ref<InputEvent> &p_event); void edit(Node *p_collision_polygon); LightOccluder2DEditor(EditorNode *p_editor); @@ -97,7 +97,7 @@ class LightOccluder2DEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return light_occluder_editor->forward_gui_input(p_event); } - virtual void forward_draw_over_canvas(Control *p_canvas) { return light_occluder_editor->forward_draw_over_canvas(p_canvas); } + virtual void forward_draw_over_viewport(Control *p_overlay) { return light_occluder_editor->forward_draw_over_viewport(p_overlay); } virtual String get_name() const { return "LightOccluder2D"; } bool has_main_screen() const { return false; } diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp index 2174f08e23..5e811bfa11 100644 --- a/editor/plugins/path_2d_editor_plugin.cpp +++ b/editor/plugins/path_2d_editor_plugin.cpp @@ -269,7 +269,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return false; } -void Path2DEditor::forward_draw_over_canvas(Control *p_canvas) { +void Path2DEditor::forward_draw_over_viewport(Control *p_overlay) { if (!node) return; diff --git a/editor/plugins/path_2d_editor_plugin.h b/editor/plugins/path_2d_editor_plugin.h index 516e48c471..638d933797 100644 --- a/editor/plugins/path_2d_editor_plugin.h +++ b/editor/plugins/path_2d_editor_plugin.h @@ -94,7 +94,7 @@ protected: public: bool forward_gui_input(const Ref<InputEvent> &p_event); - void forward_draw_over_canvas(Control *p_canvas); + void forward_draw_over_viewport(Control *p_overlay); void edit(Node *p_path2d); Path2DEditor(EditorNode *p_editor); }; @@ -108,7 +108,7 @@ class Path2DEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return path2d_editor->forward_gui_input(p_event); } - virtual void forward_draw_over_canvas(Control *p_canvas) { return path2d_editor->forward_draw_over_canvas(p_canvas); } + virtual void forward_draw_over_viewport(Control *p_overlay) { return path2d_editor->forward_draw_over_viewport(p_overlay); } virtual String get_name() const { return "Path2D"; } bool has_main_screen() const { return false; } diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 9636df970a..9fd41c1064 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -2332,6 +2332,16 @@ static void draw_indicator_bar(Control &surface, real_t fill, Ref<Texture> icon) void SpatialEditorViewport::_draw() { + EditorPluginList *over_plugin_list = EditorNode::get_singleton()->get_editor_plugins_over(); + if (!over_plugin_list->empty()) { + over_plugin_list->forward_draw_over_viewport(surface); + } + + EditorPluginList *force_over_plugin_list = editor->get_editor_plugins_force_over(); + if (!force_over_plugin_list->empty()) { + force_over_plugin_list->forward_force_draw_over_viewport(surface); + } + if (surface->has_focus()) { Size2 size = surface->get_size(); Rect2 r = Rect2(Point2(), size); @@ -2839,6 +2849,20 @@ void SpatialEditorViewport::set_state(const Dictionary &p_state) { camera->set_doppler_tracking(doppler ? Camera::DOPPLER_TRACKING_IDLE_STEP : Camera::DOPPLER_TRACKING_DISABLED); view_menu->get_popup()->set_item_checked(idx, doppler); } + if (p_state.has("gizmos")) { + bool gizmos = p_state["gizmos"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS); + if (view_menu->get_popup()->is_item_checked(idx) != gizmos) + _menu_option(VIEW_GIZMOS); + } + if (p_state.has("information")) { + bool information = p_state["information"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_INFORMATION); + if (view_menu->get_popup()->is_item_checked(idx) != information) + _menu_option(VIEW_INFORMATION); + } if (p_state.has("half_res")) { bool half_res = p_state["half_res"]; @@ -2870,6 +2894,9 @@ Dictionary SpatialEditorViewport::get_state() const { d["use_environment"] = camera->get_environment().is_valid(); d["use_orthogonal"] = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL; d["listener"] = viewport->is_audio_listener(); + d["doppler"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER)); + d["gizmos"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS)); + d["information"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION)); d["half_res"] = viewport_container->get_stretch_shrink() > 1; if (previewing) { d["previewing"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(previewing); diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 58c464c3ea..ab26a70f7f 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -311,6 +311,7 @@ protected: static void _bind_methods(); public: + void update_surface() { surface->update(); } void update_transform_gizmo_view(); void set_can_preview(Camera *p_preview); @@ -389,6 +390,8 @@ class SpatialEditor : public VBoxContainer { GDCLASS(SpatialEditor, VBoxContainer); public: + static const unsigned int VIEWPORTS_COUNT = 4; + enum ToolMode { TOOL_MODE_SELECT, @@ -403,8 +406,6 @@ public: }; private: - static const unsigned int VIEWPORTS_COUNT = 4; - EditorNode *editor; EditorSelection *editor_selection; diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index b3bb103577..175655119f 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -394,6 +394,7 @@ void SpriteFramesEditor::_animation_add() { edited_anim = name; undo_redo->commit_action(); + animations->grab_focus(); } void SpriteFramesEditor::_animation_remove() { diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 4092fd3994..e552e24c17 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -1183,7 +1183,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return false; } -void TileMapEditor::forward_draw_over_canvas(Control *p_canvas) { +void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { if (!node) return; diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index c7a5bf0cc6..ce58cc9708 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -184,7 +184,7 @@ public: HBoxContainer *get_toolbar() const { return toolbar; } bool forward_gui_input(const Ref<InputEvent> &p_event); - void forward_draw_over_canvas(Control *p_canvas); + void forward_draw_over_viewport(Control *p_overlay); void edit(Node *p_tile_map); @@ -200,7 +200,7 @@ class TileMapEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return tile_map_editor->forward_gui_input(p_event); } - virtual void forward_draw_over_canvas(Control *p_canvas) { tile_map_editor->forward_draw_over_canvas(p_canvas); } + virtual void forward_draw_over_viewport(Control *p_overlay) { tile_map_editor->forward_draw_over_viewport(p_overlay); } virtual String get_name() const { return "TileMap"; } bool has_main_screen() const { return false; } diff --git a/editor/project_export.cpp b/editor/project_export.cpp index 6500b10a3a..767dbcc27b 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -723,6 +723,7 @@ void ProjectExportDialog::_export_project() { export_project->add_filter("*." + extension + " ; " + platform->get_name() + " Export"); } + export_project->set_mode(FileDialog::MODE_SAVE_FILE); export_project->popup_centered_ratio(); } diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 46c47d8656..43c7f33cbe 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -655,6 +655,12 @@ void ProjectManager::_notification(int p_what) { Engine::get_singleton()->set_editor_hint(false); + } else if (p_what == NOTIFICATION_READY) { + + if (scroll_childs->get_child_count() == 0) { + open_templates->popup_centered_minsize(); + } + } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { set_process_unhandled_input(is_visible_in_tree()); @@ -1432,9 +1438,15 @@ void ProjectManager::_bind_methods() { ClassDB::bind_method("_favorite_pressed", &ProjectManager::_favorite_pressed); ClassDB::bind_method("_install_project", &ProjectManager::_install_project); ClassDB::bind_method("_files_dropped", &ProjectManager::_files_dropped); + ClassDB::bind_method("_open_asset_library", &ProjectManager::_open_asset_library); ClassDB::bind_method(D_METHOD("_scan_multiple_folders", "files"), &ProjectManager::_scan_multiple_folders); } +void ProjectManager::_open_asset_library() { + asset_library->disable_community_support(); + tabs->set_current_tab(1); +} + ProjectManager::ProjectManager() { // load settings @@ -1705,6 +1717,12 @@ ProjectManager::ProjectManager() { dialog_error = memnew(AcceptDialog); gui_base->add_child(dialog_error); + + open_templates = memnew(ConfirmationDialog); + open_templates->set_text(TTR("You don't currently have any projects.\nWould you like to explore the official example projects in the Asset Library?")); + open_templates->get_ok()->set_text(TTR("Open Asset Library")); + open_templates->connect("confirmed", this, "_open_asset_library"); + add_child(open_templates); } ProjectManager::~ProjectManager() { @@ -1756,6 +1774,7 @@ void ProjectListFilter::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { clear_search_button->set_icon(get_icon("Close", "EditorIcons")); + } break; } } diff --git a/editor/project_manager.h b/editor/project_manager.h index 656bd2d19c..4080f1df22 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -76,6 +76,9 @@ class ProjectManager : public Control { Control *gui_base; + ConfirmationDialog *open_templates; + + void _open_asset_library(); void _scan_projects(); void _run_project(); void _run_project_confirm(); diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index bc7d8f4b14..d383e54f05 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -2668,7 +2668,12 @@ TreeItem *PropertyEditor::get_parent_node(String p_path, HashMap<String, TreeIte if (use_folding) { if (!obj->editor_is_section_unfolded(p_path)) { updating_folding = true; - item->set_collapsed(true); + if (folding_behaviour == FB_COLLAPSEALL) + item->set_collapsed(true); + else if (folding_behaviour == FB_EXPANDALL) + item->set_collapsed(false); + else + item->set_collapsed(true); updating_folding = false; } item->set_metadata(0, p_path); @@ -4207,12 +4212,31 @@ void PropertyEditor::set_subsection_selectable(bool p_selectable) { update_tree(); } +bool PropertyEditor::is_expand_all_properties_enabled() const { + + return (use_folding == false); +} + void PropertyEditor::set_use_folding(bool p_enable) { use_folding = p_enable; tree->set_hide_folding(false); } +void PropertyEditor::collapse_all_parent_nodes() { + + folding_behaviour = FB_COLLAPSEALL; + update_tree(); + folding_behaviour = FB_UNDEFINED; +} + +void PropertyEditor::expand_all_parent_nodes() { + + folding_behaviour = FB_EXPANDALL; + update_tree(); + folding_behaviour = FB_UNDEFINED; +} + PropertyEditor::PropertyEditor() { _prop_edited = "property_edited"; @@ -4285,6 +4309,7 @@ PropertyEditor::PropertyEditor() { subsection_selectable = false; property_selectable = false; show_type_icons = false; // maybe one day will return. + folding_behaviour = FB_UNDEFINED; } PropertyEditor::~PropertyEditor() { @@ -4596,21 +4621,24 @@ SectionedPropertyEditor::~SectionedPropertyEditor() { double PropertyValueEvaluator::eval(const String &p_text) { + // If range value contains a comma replace it with dot (issue #6028) + const String &p_new_text = p_text.replace(",", "."); + if (!obj || !script_language) - return _default_eval(p_text); + return _default_eval(p_new_text); Ref<Script> script = Ref<Script>(script_language->create_script()); - script->set_source_code(_build_script(p_text)); + script->set_source_code(_build_script(p_new_text)); Error err = script->reload(); if (err) { - print_line("[PropertyValueEvaluator] Error loading script for expression: " + p_text); - return _default_eval(p_text); + print_line("[PropertyValueEvaluator] Error loading script for expression: " + p_new_text); + return _default_eval(p_new_text); } Object dummy; ScriptInstance *script_instance = script->instance_create(&dummy); if (!script_instance) - return _default_eval(p_text); + return _default_eval(p_new_text); Variant::CallError call_err; Variant arg = obj; @@ -4621,7 +4649,7 @@ double PropertyValueEvaluator::eval(const String &p_text) { } print_line("[PropertyValueEvaluator]: Error eval! Error code: " + itos(call_err.error)); - return _default_eval(p_text); + return _default_eval(p_new_text); } void PropertyValueEvaluator::edit(Object *p_obj) { diff --git a/editor/property_editor.h b/editor/property_editor.h index e69ca8bcd5..299e8d3cd7 100644 --- a/editor/property_editor.h +++ b/editor/property_editor.h @@ -1,4 +1,4 @@ -/*************************************************************************/ +/*************************************************************************/ /* property_editor.h */ /*************************************************************************/ /* This file is part of: */ @@ -206,6 +206,14 @@ class PropertyEditor : public Control { bool updating_folding; + enum FOLDING_BEHAVIOUR { + FB_UNDEFINED, + FB_COLLAPSEALL, + FB_EXPANDALL, + FB_EXPANDALL_FORCE + }; + FOLDING_BEHAVIOUR folding_behaviour; + HashMap<String, String> pending; String selected_property; @@ -304,6 +312,11 @@ public: void set_property_selectable(bool p_selectable); void set_use_folding(bool p_enable); + + bool is_expand_all_properties_enabled() const; + + void collapse_all_parent_nodes(); + void expand_all_parent_nodes(); PropertyEditor(); ~PropertyEditor(); }; diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index b23d914b88..4d86030e7d 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1903,6 +1903,7 @@ void SceneTreeDock::_bind_methods() { SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data) { + set_name("Scene"); editor = p_editor; edited_scene = NULL; editor_data = &p_editor_data; diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index dfda8a780d..2c0981ca30 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -481,6 +481,12 @@ void SceneTreeEditor::_selected_changed() { blocked--; } +void SceneTreeEditor::_deselect_items() { + + // Clear currently elected items in scene tree dock. + editor_selection->clear(); +} + void SceneTreeEditor::_cell_multi_selected(Object *p_object, int p_cell, bool p_selected) { TreeItem *item = Object::cast_to<TreeItem>(p_object); @@ -929,6 +935,7 @@ void SceneTreeEditor::_bind_methods() { ClassDB::bind_method("_update_tree", &SceneTreeEditor::_update_tree); ClassDB::bind_method("_node_removed", &SceneTreeEditor::_node_removed); ClassDB::bind_method("_selected_changed", &SceneTreeEditor::_selected_changed); + ClassDB::bind_method("_deselect_items", &SceneTreeEditor::_deselect_items); ClassDB::bind_method("_renamed", &SceneTreeEditor::_renamed); ClassDB::bind_method("_rename_node", &SceneTreeEditor::_rename_node); ClassDB::bind_method("_test_update_tree", &SceneTreeEditor::_test_update_tree); @@ -1005,6 +1012,7 @@ SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_ope tree->connect("item_edited", this, "_renamed", varray(), CONNECT_DEFERRED); tree->connect("multi_selected", this, "_cell_multi_selected"); tree->connect("button_pressed", this, "_cell_button_pressed"); + tree->connect("nothing_selected", this, "_deselect_items"); //tree->connect("item_edited", this,"_renamed",Vector<Variant>(),true); error = memnew(AcceptDialog); diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h index 1114b92796..88d60f9b8a 100644 --- a/editor/scene_tree_editor.h +++ b/editor/scene_tree_editor.h @@ -78,6 +78,7 @@ class SceneTreeEditor : public Control { TreeItem *_find(TreeItem *p_node, const NodePath &p_path); void _notification(int p_what); void _selected_changed(); + void _deselect_items(); void _rename_node(ObjectID p_node, const String &p_name); void _cell_collapsed(Object *p_obj); diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index a06732c563..7705e4e1b9 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -45,6 +45,7 @@ #include "scene/gui/tab_container.h" #include "scene/gui/texture_button.h" #include "scene/gui/tree.h" +#include "ustring.h" class ScriptEditorDebuggerVariables : public Object { @@ -642,7 +643,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da v /= 1024.0; } tt += " bytes"; - vs = rtos(v) + " " + unit; + vs = String::num(v, 2) + " " + unit; } break; case Performance::MONITOR_TYPE_TIME: { tt += " seconds"; |