diff options
Diffstat (limited to 'editor')
81 files changed, 2139 insertions, 1145 deletions
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 96931efd3b..49c79d709b 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -61,29 +61,37 @@ static const char *_joy_axis_descriptions[(size_t)JoyAxis::MAX * 2] = { TTRC("Joystick 4 Down"), }; -String InputEventConfigurationDialog::get_event_text(const Ref<InputEvent> &p_event) { +String InputEventConfigurationDialog::get_event_text(const Ref<InputEvent> &p_event, bool p_include_device) const { ERR_FAIL_COND_V_MSG(p_event.is_null(), String(), "Provided event is not a valid instance of InputEvent"); - // Joypad motion events will display slightly differently than what the event->as_text() provides. See #43660. - Ref<InputEventJoypadMotion> jpmotion = p_event; - if (jpmotion.is_valid()) { + String text = p_event->as_text(); + + Ref<InputEventMouse> mouse = p_event; + Ref<InputEventJoypadMotion> jp_motion = p_event; + Ref<InputEventJoypadButton> jp_button = p_event; + if (jp_motion.is_valid()) { + // Joypad motion events will display slightly differently than what the event->as_text() provides. See #43660. String desc = TTR("Unknown Joypad Axis"); - if (jpmotion->get_axis() < JoyAxis::MAX) { - desc = RTR(_joy_axis_descriptions[2 * (size_t)jpmotion->get_axis() + (jpmotion->get_axis_value() < 0 ? 0 : 1)]); + if (jp_motion->get_axis() < JoyAxis::MAX) { + desc = RTR(_joy_axis_descriptions[2 * (size_t)jp_motion->get_axis() + (jp_motion->get_axis_value() < 0 ? 0 : 1)]); } - return vformat("Joypad Axis %s %s (%s)", itos((int64_t)jpmotion->get_axis()), jpmotion->get_axis_value() < 0 ? "-" : "+", desc); - } else { - return p_event->as_text(); + text = vformat("Joypad Axis %s %s (%s)", itos((int64_t)jp_motion->get_axis()), jp_motion->get_axis_value() < 0 ? "-" : "+", desc); + } + if (p_include_device && (mouse.is_valid() || jp_button.is_valid() || jp_motion.is_valid())) { + String device_string = _get_device_string(p_event->get_device()); + text += vformat(" - %s", device_string); } + + return text; } -void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event) { +void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, bool p_update_input_list_selection) { if (p_event.is_valid()) { event = p_event; // Update Label - event_as_text->set_text(get_event_text(event)); + event_as_text->set_text(get_event_text(event, true)); Ref<InputEventKey> k = p_event; Ref<InputEventMouseButton> mb = p_event; @@ -122,7 +130,7 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event) { additional_options_container->show(); // Update selected item in input list. - if (k.is_valid() || joyb.is_valid() || joym.is_valid() || mb.is_valid()) { + if (p_update_input_list_selection && (k.is_valid() || joyb.is_valid() || joym.is_valid() || mb.is_valid())) { TreeItem *category = input_list_tree->get_root()->get_first_child(); while (category) { TreeItem *input_item = category->get_first_child(); @@ -234,10 +242,13 @@ void InputEventConfigurationDialog::_listen_window_input(const Ref<InputEvent> & } } + // Create an editable reference + Ref<InputEvent> received_event = p_event; + // Check what the type is and if it is allowed. - Ref<InputEventKey> k = p_event; - Ref<InputEventJoypadButton> joyb = p_event; - Ref<InputEventJoypadMotion> joym = p_event; + Ref<InputEventKey> k = received_event; + Ref<InputEventJoypadButton> joyb = received_event; + Ref<InputEventJoypadMotion> joym = received_event; int type = 0; if (k.is_valid()) { @@ -266,7 +277,7 @@ void InputEventConfigurationDialog::_listen_window_input(const Ref<InputEvent> & } if (k.is_valid()) { - k->set_pressed(false); // to avoid serialisation of 'pressed' property - doesn't matter for actions anyway. + k->set_pressed(false); // To avoid serialisation of 'pressed' property - doesn't matter for actions anyway. // Maintain physical keycode option state if (physical_key_checkbox->is_pressed()) { k->set_keycode(Key::NONE); @@ -275,15 +286,17 @@ void InputEventConfigurationDialog::_listen_window_input(const Ref<InputEvent> & } } - Ref<InputEventWithModifiers> mod = p_event; + Ref<InputEventWithModifiers> mod = received_event; if (mod.is_valid()) { // Maintain store command option state mod->set_store_command(store_command_checkbox->is_pressed()); - mod->set_window_id(0); } - _set_event(p_event); + // Maintain device selection. + received_event->set_device(_get_current_device()); + + _set_event(received_event); set_input_as_handled(); } @@ -331,7 +344,7 @@ void InputEventConfigurationDialog::_update_input_list() { Ref<InputEventMouseButton> mb; mb.instantiate(); mb->set_button_index(mouse_buttons[i]); - String desc = get_event_text(mb); + String desc = get_event_text(mb, false); if (!search_term.is_empty() && desc.findn(search_term) == -1) { continue; @@ -354,7 +367,7 @@ void InputEventConfigurationDialog::_update_input_list() { Ref<InputEventJoypadButton> joyb; joyb.instantiate(); joyb->set_button_index((JoyButton)i); - String desc = get_event_text(joyb); + String desc = get_event_text(joyb, false); if (!search_term.is_empty() && desc.findn(search_term) == -1) { continue; @@ -380,7 +393,7 @@ void InputEventConfigurationDialog::_update_input_list() { joym.instantiate(); joym->set_axis((JoyAxis)axis); joym->set_axis_value(direction); - String desc = get_event_text(joym); + String desc = get_event_text(joym, false); if (!search_term.is_empty() && desc.findn(search_term) == -1) { continue; @@ -495,7 +508,7 @@ void InputEventConfigurationDialog::_input_list_item_selected() { k->set_meta_pressed(mod_checkboxes[MOD_META]->is_pressed()); k->set_store_command(store_command_checkbox->is_pressed()); - _set_event(k); + _set_event(k, false); } break; case InputEventConfigurationDialog::INPUT_MOUSE_BUTTON: { MouseButton idx = (MouseButton)(int)selected->get_meta("__index"); @@ -510,12 +523,19 @@ void InputEventConfigurationDialog::_input_list_item_selected() { mb->set_meta_pressed(mod_checkboxes[MOD_META]->is_pressed()); mb->set_store_command(store_command_checkbox->is_pressed()); - _set_event(mb); + // Maintain selected device + mb->set_device(_get_current_device()); + + _set_event(mb, false); } break; case InputEventConfigurationDialog::INPUT_JOY_BUTTON: { JoyButton idx = (JoyButton)(int)selected->get_meta("__index"); Ref<InputEventJoypadButton> jb = InputEventJoypadButton::create_reference(idx); - _set_event(jb); + + // Maintain selected device + jb->set_device(_get_current_device()); + + _set_event(jb, false); } break; case InputEventConfigurationDialog::INPUT_JOY_MOTION: { JoyAxis axis = (JoyAxis)(int)selected->get_meta("__axis"); @@ -525,24 +545,35 @@ void InputEventConfigurationDialog::_input_list_item_selected() { jm.instantiate(); jm->set_axis(axis); jm->set_axis_value(value); - _set_event(jm); + + // Maintain selected device + jm->set_device(_get_current_device()); + + _set_event(jm, false); } break; } } -void InputEventConfigurationDialog::_set_current_device(int i_device) { - device_id_option->select(i_device + 1); +void InputEventConfigurationDialog::_device_selection_changed(int p_option_button_index) { + // Subtract 1 as option index 0 corresponds to "All Devices" (value of -1) + // and option index 1 corresponds to device 0, etc... + event->set_device(p_option_button_index - 1); + event_as_text->set_text(get_event_text(event, true)); +} + +void InputEventConfigurationDialog::_set_current_device(int p_device) { + device_id_option->select(p_device + 1); } int InputEventConfigurationDialog::_get_current_device() const { return device_id_option->get_selected() - 1; } -String InputEventConfigurationDialog::_get_device_string(int i_device) const { - if (i_device == InputMap::ALL_DEVICES) { +String InputEventConfigurationDialog::_get_device_string(int p_device) const { + if (p_device == InputMap::ALL_DEVICES) { return TTR("All Devices"); } - return TTR("Device") + " " + itos(i_device); + return TTR("Device") + " " + itos(p_device); } void InputEventConfigurationDialog::_notification(int p_what) { @@ -558,8 +589,6 @@ void InputEventConfigurationDialog::_notification(int p_what) { icon_cache.joypad_button = get_theme_icon(SNAME("JoyButton"), SNAME("EditorIcons")); icon_cache.joypad_axis = get_theme_icon(SNAME("JoyAxis"), SNAME("EditorIcons")); - mouse_detection_rect->set_color(get_theme_color(SNAME("dark_color_2"), SNAME("Editor"))); - _update_input_list(); } break; } @@ -588,9 +617,12 @@ void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p // Switch to "Listen" tab tab_container->set_current_tab(0); + + // Select "All Devices" by default. + device_id_option->select(0); } - popup_centered(); + popup_centered(Size2(0, 400) * EDSCALE); } Ref<InputEvent> InputEventConfigurationDialog::get_event() const { @@ -622,8 +654,8 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() { event_as_text = memnew(Label); event_as_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); vb->add_child(event_as_text); - // Mouse button detection rect (Mouse button event outside this ColorRect will be ignored) - mouse_detection_rect = memnew(ColorRect); + // Mouse button detection rect (Mouse button event outside this rect will be ignored) + mouse_detection_rect = memnew(Panel); mouse_detection_rect->set_v_size_flags(Control::SIZE_EXPAND_FILL); vb->add_child(mouse_detection_rect); tab_container->add_child(vb); @@ -673,12 +705,13 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() { device_id_option = memnew(OptionButton); device_id_option->set_h_size_flags(Control::SIZE_EXPAND_FILL); - device_container->add_child(device_id_option); - for (int i = -1; i < 8; i++) { device_id_option->add_item(_get_device_string(i)); } - _set_current_device(0); + device_id_option->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_device_selection_changed)); + _set_current_device(InputMap::ALL_DEVICES); + device_container->add_child(device_id_option); + device_container->hide(); additional_options_container->add_child(device_container); @@ -1096,7 +1129,7 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info TreeItem *event_item = action_tree->create_item(action_item); // First Column - Text - event_item->set_text(0, event_config_dialog->get_event_text(event)); // Need to us the special description for JoypadMotion here, so don't use as_text() directly. + event_item->set_text(0, event_config_dialog->get_event_text(event, true)); event_item->set_meta("__event", event); event_item->set_meta("__index", evnt_idx); @@ -1136,7 +1169,7 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info void ActionMapEditor::show_message(const String &p_message) { message->set_text(p_message); - message->popup_centered(Size2(300, 100) * EDSCALE); + message->popup_centered(); } void ActionMapEditor::use_external_search_box(LineEdit *p_searchbox) { diff --git a/editor/action_map_editor.h b/editor/action_map_editor.h index e96139e070..b676c55403 100644 --- a/editor/action_map_editor.h +++ b/editor/action_map_editor.h @@ -67,7 +67,7 @@ private: // Listening for input Label *event_as_text = nullptr; - ColorRect *mouse_detection_rect = nullptr; + Panel *mouse_detection_rect = nullptr; // List of All Key/Mouse/Joypad input options. int allowed_input_types; @@ -97,7 +97,7 @@ private: CheckBox *physical_key_checkbox = nullptr; - void _set_event(const Ref<InputEvent> &p_event); + void _set_event(const Ref<InputEvent> &p_event, bool p_update_input_list_selection = true); void _tab_selected(int p_tab); void _listen_window_input(const Ref<InputEvent> &p_event); @@ -110,9 +110,10 @@ private: void _store_command_toggled(bool p_checked); void _physical_keycode_toggled(bool p_checked); - void _set_current_device(int i_device); + void _device_selection_changed(int p_option_button_index); + void _set_current_device(int p_device); int _get_current_device() const; - String _get_device_string(int i_device) const; + String _get_device_string(int p_device) const; protected: void _notification(int p_what); @@ -121,7 +122,7 @@ public: // Pass an existing event to configure it. Alternatively, pass no event to start with a blank configuration. void popup_and_configure(const Ref<InputEvent> &p_event = Ref<InputEvent>()); Ref<InputEvent> get_event() const; - String get_event_text(const Ref<InputEvent> &p_event); + String get_event_text(const Ref<InputEvent> &p_event, bool p_include_device) const; void set_allowed_input_types(int p_type_masks); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index dc69a8cb08..685dde4d98 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -1961,11 +1961,21 @@ void AnimationTrackEdit::_notification(int p_what) { int limit = timeline->get_name_limit(); + if (track % 2 == 1) { + // Draw a background over odd lines to make long lists of tracks easier to read. + draw_rect(Rect2(Point2(1 * EDSCALE, 0), get_size() - Size2(1 * EDSCALE, 0)), Color(0.5, 0.5, 0.5, 0.05)); + } + + if (hovered) { + // Draw hover feedback. + draw_rect(Rect2(Point2(1 * EDSCALE, 0), get_size() - Size2(1 * EDSCALE, 0)), Color(0.5, 0.5, 0.5, 0.1)); + } + if (has_focus()) { Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor")); accent.a *= 0.7; // Offside so the horizontal sides aren't cutoff. - draw_rect(Rect2(Point2(1 * EDSCALE, 0), get_size() - Size2(1 * EDSCALE, 0)), accent, false); + draw_style_box(get_theme_stylebox(SNAME("Focus"), SNAME("EditorStyles")), Rect2(Point2(1 * EDSCALE, 0), get_size() - Size2(1 * EDSCALE, 0))); } Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label")); @@ -2236,7 +2246,16 @@ void AnimationTrackEdit::_notification(int p_what) { } } break; + case NOTIFICATION_MOUSE_ENTER: + hovered = true; + update(); + break; case NOTIFICATION_MOUSE_EXIT: + hovered = false; + // When the mouse cursor exits the track, we're no longer hovering any keyframe. + hovering_key_idx = -1; + update(); + [[fallthrough]]; case NOTIFICATION_DRAG_END: { cancel_drop(); } break; @@ -2348,7 +2367,13 @@ void AnimationTrackEdit::draw_key(int p_index, float p_pixels_sec, int p_x, bool } } - draw_texture(icon_to_draw, ofs); + // Use a different color for the currently hovered key. + // The color multiplier is chosen to work with both dark and light editor themes, + // and on both unselected and selected key icons. + draw_texture( + icon_to_draw, + ofs, + p_index == hovering_key_idx ? get_theme_color(SNAME("folder_icon_modulate"), SNAME("FileDialog")) : Color(1, 1, 1)); } // Helper. @@ -2935,6 +2960,59 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) { } Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + const int previous_hovering_key_idx = hovering_key_idx; + + // Hovering compressed keyframes for editing is not possible. + if (!animation->track_is_compressed(track)) { + const float scale = timeline->get_zoom_scale(); + const int limit = timeline->get_name_limit(); + const int limit_end = get_size().width - timeline->get_buttons_width(); + // Left Border including space occupied by keyframes on t=0. + const int limit_start_hitbox = limit - type_icon->get_width(); + const Point2 pos = mm->get_position(); + + if (pos.x >= limit_start_hitbox && pos.x <= limit_end) { + // Use the same logic as key selection to ensure that hovering accurately represents + // which key will be selected when clicking. + int key_idx = -1; + float key_distance = 1e20; + + hovering_key_idx = -1; + + // Hovering should happen in the opposite order of drawing for more accurate overlap hovering. + for (int i = animation->track_get_key_count(track) - 1; i >= 0; i--) { + Rect2 rect = get_key_rect(i, scale); + float offset = animation->track_get_key_time(track, i) - timeline->get_value(); + offset = offset * scale + limit; + rect.position.x += offset; + + if (rect.has_point(pos)) { + if (is_key_selectable_by_distance()) { + const float distance = ABS(offset - pos.x); + if (key_idx == -1 || distance < key_distance) { + key_idx = i; + key_distance = distance; + hovering_key_idx = i; + } + } else { + // First one does it. + hovering_key_idx = i; + break; + } + } + } + + print_line(hovering_key_idx); + + if (hovering_key_idx != previous_hovering_key_idx) { + // Required to draw keyframe hover feedback on the correct keyframe. + update(); + } + } + } + } + if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE && moving_selection_attempt) { if (!moving_selection) { moving_selection = true; diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index d025dd3d26..0f6d12b4d4 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -171,7 +171,9 @@ class AnimationTrackEdit : public Control { PopupMenu *menu = nullptr; + bool hovered = false; bool clicking_on_name = false; + int hovering_key_idx = -1; void _zoom_changed(); diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index f9858aa514..fbb61a1614 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -377,17 +377,17 @@ void CreateDialog::_confirmed() { return; } - FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("create_recent." + base_type), FileAccess::WRITE); - if (f) { - f->store_line(selected_item); - - for (int i = 0; i < MIN(32, recent->get_item_count()); i++) { - if (recent->get_item_text(i) != selected_item) { - f->store_line(recent->get_item_text(i)); + { + Ref<FileAccess> f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("create_recent." + base_type), FileAccess::WRITE); + if (f.is_valid()) { + f->store_line(selected_item); + + for (int i = 0; i < MIN(32, recent->get_item_count()); i++) { + if (recent->get_item_text(i) != selected_item) { + f->store_line(recent->get_item_text(i)); + } } } - - memdelete(f); } // To prevent, emitting an error from the transient window (shader dialog for example) hide this dialog before emitting the "create" signal. @@ -647,25 +647,26 @@ void CreateDialog::_save_and_update_favorite_list() { favorites->clear(); TreeItem *root = favorites->create_item(); - FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites." + base_type), FileAccess::WRITE); - if (f) { - for (int i = 0; i < favorite_list.size(); i++) { - String l = favorite_list[i]; - String name = l.get_slicec(' ', 0); - if (!(ClassDB::class_exists(name) || ScriptServer::is_global_class(name))) { - continue; - } - f->store_line(l); + { + Ref<FileAccess> f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites." + base_type), FileAccess::WRITE); + if (f.is_valid()) { + for (int i = 0; i < favorite_list.size(); i++) { + String l = favorite_list[i]; + String name = l.get_slicec(' ', 0); + if (!(ClassDB::class_exists(name) || ScriptServer::is_global_class(name))) { + continue; + } + f->store_line(l); - if (_is_class_disabled_by_feature_profile(name)) { - continue; - } + if (_is_class_disabled_by_feature_profile(name)) { + continue; + } - TreeItem *ti = favorites->create_item(root); - ti->set_text(0, l); - ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(name, icon_fallback)); + TreeItem *ti = favorites->create_item(root); + ti->set_text(0, l); + ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(name, icon_fallback)); + } } - memdelete(f); } emit_signal(SNAME("favorites_updated")); @@ -673,8 +674,8 @@ void CreateDialog::_save_and_update_favorite_list() { void CreateDialog::_load_favorites_and_history() { String dir = EditorSettings::get_singleton()->get_project_settings_dir(); - FileAccess *f = FileAccess::open(dir.plus_file("create_recent." + base_type), FileAccess::READ); - if (f) { + Ref<FileAccess> f = FileAccess::open(dir.plus_file("create_recent." + base_type), FileAccess::READ); + if (f.is_valid()) { while (!f->eof_reached()) { String l = f->get_line().strip_edges(); String name = l.get_slicec(' ', 0); @@ -683,12 +684,10 @@ void CreateDialog::_load_favorites_and_history() { recent->add_item(l, EditorNode::get_singleton()->get_class_icon(name, icon_fallback)); } } - - memdelete(f); } f = FileAccess::open(dir.plus_file("favorites." + base_type), FileAccess::READ); - if (f) { + if (f.is_valid()) { while (!f->eof_reached()) { String l = f->get_line().strip_edges(); @@ -696,8 +695,6 @@ void CreateDialog::_load_favorites_and_history() { favorite_list.push_back(l); } } - - memdelete(f); } } diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 1e8753acc0..3a3b35f8a5 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -164,7 +164,7 @@ void ScriptEditorDebugger::_file_selected(const String &p_file) { switch (file_dialog_purpose) { case SAVE_MONITORS_CSV: { Error err; - FileAccessRef file = FileAccess::open(p_file, FileAccess::WRITE, &err); + Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err); if (err != OK) { ERR_PRINT("Failed to open " + p_file); @@ -209,7 +209,7 @@ void ScriptEditorDebugger::_file_selected(const String &p_file) { } break; case SAVE_VRAM_CSV: { Error err; - FileAccessRef file = FileAccess::open(p_file, FileAccess::WRITE, &err); + Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err); if (err != OK) { ERR_PRINT("Failed to open " + p_file); diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 95b3a02631..35f40a159a 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -749,7 +749,7 @@ void OrphanResourcesDialog::_find_to_delete(TreeItem *p_item, List<String> &path } void OrphanResourcesDialog::_delete_confirm() { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); for (const String &E : paths) { da->remove(E); EditorFileSystem::get_singleton()->update_file(E); diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index adad8fdba8..16cbc0f34d 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -1035,8 +1035,8 @@ static Error _parse_methods(Ref<XMLParser> &parser, Vector<DocData::MethodDoc> & Error DocTools::load_classes(const String &p_dir) { Error err; - DirAccessRef da = DirAccess::open(p_dir, &err); - if (!da) { + Ref<DirAccess> da = DirAccess::open(p_dir, &err); + if (da.is_null()) { return err; } @@ -1063,8 +1063,8 @@ Error DocTools::load_classes(const String &p_dir) { Error DocTools::erase_classes(const String &p_dir) { Error err; - DirAccessRef da = DirAccess::open(p_dir, &err); - if (!da) { + Ref<DirAccess> da = DirAccess::open(p_dir, &err); + if (da.is_null()) { return err; } @@ -1273,7 +1273,7 @@ Error DocTools::_load(Ref<XMLParser> parser) { return OK; } -static void _write_string(FileAccess *f, int p_tablevel, const String &p_string) { +static void _write_string(Ref<FileAccess> f, int p_tablevel, const String &p_string) { if (p_string.is_empty()) { return; } @@ -1284,7 +1284,7 @@ static void _write_string(FileAccess *f, int p_tablevel, const String &p_string) f->store_string(tab + p_string + "\n"); } -static void _write_method_doc(FileAccess *f, const String &p_name, Vector<DocData::MethodDoc> &p_method_docs) { +static void _write_method_doc(Ref<FileAccess> f, const String &p_name, Vector<DocData::MethodDoc> &p_method_docs) { if (!p_method_docs.is_empty()) { p_method_docs.sort(); _write_string(f, 1, "<" + p_name + "s>"); @@ -1350,7 +1350,7 @@ Error DocTools::save_classes(const String &p_default_path, const Map<String, Str Error err; String save_file = save_path.plus_file(c.name + ".xml"); - FileAccessRef f = FileAccess::open(save_file, FileAccess::WRITE, &err); + Ref<FileAccess> f = FileAccess::open(save_file, FileAccess::WRITE, &err); ERR_CONTINUE_MSG(err != OK, "Can't write doc file: " + save_file + "."); diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp index 2537c4d4a8..7dcb9a4088 100644 --- a/editor/editor_asset_installer.cpp +++ b/editor/editor_asset_installer.cpp @@ -64,8 +64,7 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) { package_path = p_path; Set<String> files_sorted; - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + zlib_filefunc_def io = zipio_create_io(); unzFile pkg = unzOpen2(p_path.utf8().get_data(), &io); if (!pkg) { @@ -238,8 +237,7 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) { } void EditorAssetInstaller::ok_pressed() { - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + zlib_filefunc_def io = zipio_create_io(); unzFile pkg = unzOpen2(package_path.utf8().get_data(), &io); if (!pkg) { @@ -280,7 +278,7 @@ void EditorAssetInstaller::ok_pressed() { dirpath = dirpath.substr(0, dirpath.length() - 1); } - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); da->make_dir(dirpath); } else { Vector<uint8_t> data; @@ -291,10 +289,9 @@ void EditorAssetInstaller::ok_pressed() { unzReadCurrentFile(pkg, data.ptrw(), data.size()); unzCloseCurrentFile(pkg); - FileAccess *f = FileAccess::open(path, FileAccess::WRITE); - if (f) { + Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE); + if (f.is_valid()) { f->store_buffer(data.ptr(), data.size()); - memdelete(f); } else { failed_files.push_back(path); } diff --git a/editor/editor_dir_dialog.cpp b/editor/editor_dir_dialog.cpp index 9fabde93ef..8494991892 100644 --- a/editor/editor_dir_dialog.cpp +++ b/editor/editor_dir_dialog.cpp @@ -156,8 +156,8 @@ void EditorDirDialog::_make_dir_confirm() { String dir = ti->get_metadata(0); - DirAccessRef d = DirAccess::open(dir); - ERR_FAIL_COND_MSG(!d, "Cannot open directory '" + dir + "'."); + Ref<DirAccess> d = DirAccess::open(dir); + ERR_FAIL_COND_MSG(d.is_null(), "Cannot open directory '" + dir + "'."); const String stripped_dirname = makedirname->get_text().strip_edges(); diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index a1081fcbfb..58a9175df1 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -327,12 +327,12 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa } } - FileAccessEncrypted *fae = nullptr; - FileAccess *ftmp = pd->f; + Ref<FileAccessEncrypted> fae; + Ref<FileAccess> ftmp = pd->f; if (sd.encrypted) { - fae = memnew(FileAccessEncrypted); - ERR_FAIL_COND_V(!fae, ERR_SKIP); + fae.instantiate(); + ERR_FAIL_COND_V(fae.is_null(), ERR_SKIP); Error err = fae->open_and_parse(ftmp, p_key, FileAccessEncrypted::MODE_WRITE_AES256, false); ERR_FAIL_COND_V(err != OK, ERR_SKIP); @@ -342,9 +342,9 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa // Store file content. ftmp->store_buffer(p_data.ptr(), p_data.size()); - if (fae) { - fae->release(); - memdelete(fae); + if (fae.is_valid()) { + ftmp.unref(); + fae.unref(); } int pad = _get_pad(PCK_PADDING, pd->f->get_position()); @@ -480,7 +480,7 @@ void EditorExportPlatform::_export_find_dependencies(const String &p_path, Set<S } } -void EditorExportPlatform::_edit_files_with_filter(DirAccess *da, const Vector<String> &p_filters, Set<String> &r_list, bool exclude) { +void EditorExportPlatform::_edit_files_with_filter(Ref<DirAccess> &da, const Vector<String> &p_filters, Set<String> &r_list, bool exclude) { da->list_dir_begin(); String cur_dir = da->get_current_dir().replace("\\", "/"); if (!cur_dir.ends_with("/")) { @@ -542,10 +542,9 @@ void EditorExportPlatform::_edit_filter_list(Set<String> &r_list, const String & filters.push_back(f); } - DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - ERR_FAIL_NULL(da); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + ERR_FAIL_COND(da.is_null()); _edit_files_with_filter(da, filters, r_list, exclude); - memdelete(da); } void EditorExportPlugin::set_export_preset(const Ref<EditorExportPreset> &p_preset) { @@ -1130,12 +1129,12 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b EditorProgress ep("savepack", TTR("Packing"), 102, true); // Create the temporary export directory if it doesn't exist. - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); da->make_dir_recursive(EditorPaths::get_singleton()->get_cache_dir()); String tmppath = EditorPaths::get_singleton()->get_cache_dir().plus_file("packtmp"); - FileAccess *ftmp = FileAccess::open(tmppath, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(!ftmp, ERR_CANT_CREATE, "Cannot create file '" + tmppath + "'."); + Ref<FileAccess> ftmp = FileAccess::open(tmppath, FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(ftmp.is_null(), ERR_CANT_CREATE, "Cannot create file '" + tmppath + "'."); PackData pd; pd.ep = &ep; @@ -1144,7 +1143,9 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b Error err = export_project_files(p_preset, p_debug, _save_pack_file, &pd, _add_shared_object); - memdelete(ftmp); //close tmp file + // Close temp file. + pd.f.unref(); + ftmp.unref(); if (err != OK) { DirAccess::remove_file_or_error(tmppath); @@ -1154,19 +1155,19 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b pd.file_ofs.sort(); //do sort, so we can do binary search later - FileAccess *f; + Ref<FileAccess> f; int64_t embed_pos = 0; if (!p_embed) { // Regular output to separate PCK file f = FileAccess::open(p_path, FileAccess::WRITE); - if (!f) { + if (f.is_null()) { DirAccess::remove_file_or_error(tmppath); ERR_FAIL_V(ERR_CANT_CREATE); } } else { // Append to executable f = FileAccess::open(p_path, FileAccess::READ_WRITE); - if (!f) { + if (f.is_null()) { DirAccess::remove_file_or_error(tmppath); ERR_FAIL_V(ERR_FILE_CANT_OPEN); } @@ -1211,8 +1212,8 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b f->store_32(pd.file_ofs.size()); //amount of files - FileAccessEncrypted *fae = nullptr; - FileAccess *fhead = f; + Ref<FileAccessEncrypted> fae; + Ref<FileAccess> fhead = f; if (enc_pck && enc_directory) { String script_key = p_preset->get_script_encryption_key().to_lower(); @@ -1243,8 +1244,8 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b key.write[i] = v; } } - fae = memnew(FileAccessEncrypted); - ERR_FAIL_COND_V(!fae, ERR_SKIP); + fae.instantiate(); + ERR_FAIL_COND_V(fae.is_null(), ERR_SKIP); err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_WRITE_AES256, false); ERR_FAIL_COND_V(err != OK, ERR_SKIP); @@ -1272,9 +1273,9 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b fhead->store_32(flags); } - if (fae) { - fae->release(); - memdelete(fae); + if (fae.is_valid()) { + fhead.unref(); + fae.unref(); } int header_padding = _get_pad(PCK_PADDING, f->get_position()); @@ -1290,8 +1291,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b // Save the rest of the data. ftmp = FileAccess::open(tmppath, FileAccess::READ); - if (!ftmp) { - memdelete(f); + if (ftmp.is_null()) { DirAccess::remove_file_or_error(tmppath); ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't open file to read from path '" + String(tmppath) + "'."); } @@ -1307,7 +1307,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b f->store_buffer(buf, got); } - memdelete(ftmp); + ftmp.unref(); // Close temp file. if (p_embed) { // Ensure embedded data ends at a 64-bit multiple @@ -1326,7 +1326,6 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b } } - memdelete(f); DirAccess::remove_file_or_error(tmppath); return OK; @@ -1335,8 +1334,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { EditorProgress ep("savezip", TTR("Packing"), 102, true); - FileAccess *src_f; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + zlib_filefunc_def io = zipio_create_io(); zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io); ZipData zd; @@ -1839,7 +1837,7 @@ Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_pr return ERR_FILE_NOT_FOUND; } - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); da->make_dir_recursive(p_path.get_base_dir()); Error err = da->copy(template_path, p_path, get_chmod_flags()); diff --git a/editor/editor_export.h b/editor/editor_export.h index 4d5aebc770..236f4d129c 100644 --- a/editor/editor_export.h +++ b/editor/editor_export.h @@ -184,7 +184,7 @@ private: }; struct PackData { - FileAccess *f = nullptr; + Ref<FileAccess> f; Vector<SavedData> file_ofs; EditorProgress *ep = nullptr; Vector<SharedObject> *so_files = nullptr; @@ -207,7 +207,7 @@ private: static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); static Error _save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); - void _edit_files_with_filter(DirAccess *da, const Vector<String> &p_filters, Set<String> &r_list, bool exclude); + void _edit_files_with_filter(Ref<DirAccess> &da, const Vector<String> &p_filters, Set<String> &r_list, bool exclude); void _edit_filter_list(Set<String> &r_list, const String &p_filter, bool exclude); static Error _add_shared_object(void *p_userdata, const SharedObject &p_so); diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp index cf45848ed3..a20f112b2a 100644 --- a/editor/editor_feature_profile.cpp +++ b/editor/editor_feature_profile.cpp @@ -198,13 +198,12 @@ Error EditorFeatureProfile::save_to_file(const String &p_path) { data["disabled_features"] = dis_features; - FileAccessRef f = FileAccess::open(p_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); JSON json; String text = json.stringify(data, "\t"); f->store_string(text); - f->close(); return OK; } @@ -350,8 +349,8 @@ void EditorFeatureProfileManager::_update_profile_list(const String &p_select_pr } Vector<String> profiles; - DirAccessRef d = DirAccess::open(EditorSettings::get_singleton()->get_feature_profiles_dir()); - ERR_FAIL_COND_MSG(!d, "Cannot open directory '" + EditorSettings::get_singleton()->get_feature_profiles_dir() + "'."); + Ref<DirAccess> d = DirAccess::open(EditorSettings::get_singleton()->get_feature_profiles_dir()); + ERR_FAIL_COND_MSG(d.is_null(), "Cannot open directory '" + EditorSettings::get_singleton()->get_feature_profiles_dir() + "'."); d->list_dir_begin(); while (true) { @@ -453,8 +452,8 @@ void EditorFeatureProfileManager::_profile_action(int p_action) { void EditorFeatureProfileManager::_erase_selected_profile() { String selected = _get_selected_profile(); ERR_FAIL_COND(selected.is_empty()); - DirAccessRef da = DirAccess::open(EditorSettings::get_singleton()->get_feature_profiles_dir()); - ERR_FAIL_COND_MSG(!da, "Cannot open directory '" + EditorSettings::get_singleton()->get_feature_profiles_dir() + "'."); + Ref<DirAccess> da = DirAccess::open(EditorSettings::get_singleton()->get_feature_profiles_dir()); + ERR_FAIL_COND_MSG(da.is_null(), "Cannot open directory '" + EditorSettings::get_singleton()->get_feature_profiles_dir() + "'."); da->remove(selected + ".profile"); if (selected == current_profile) { diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index ca3e70830c..dca69ffd5f 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -1065,7 +1065,6 @@ void EditorFileDialog::set_access(Access p_access) { if (access == p_access) { return; } - memdelete(dir_access); switch (p_access) { case ACCESS_FILESYSTEM: { dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); @@ -1853,5 +1852,4 @@ EditorFileDialog::~EditorFileDialog() { if (unregister_func) { unregister_func(this); } - memdelete(dir_access); } diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h index fffe7ffcc5..db2a2ab09f 100644 --- a/editor/editor_file_dialog.h +++ b/editor/editor_file_dialog.h @@ -111,7 +111,7 @@ private: LineEdit *file = nullptr; OptionButton *filter = nullptr; AcceptDialog *error_dialog = nullptr; - DirAccess *dir_access = nullptr; + Ref<DirAccess> dir_access; ConfirmationDialog *confirm_save = nullptr; DependencyRemoveDialog *dep_remove_dialog = nullptr; ConfirmationDialog *global_remove_dialog = nullptr; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 2b98a4b02a..099dfe69d5 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -218,81 +218,80 @@ void EditorFileSystem::_scan_filesystem() { String project = ProjectSettings::get_singleton()->get_resource_path(); String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(CACHE_FILE_NAME); - FileAccess *f = FileAccess::open(fscache, FileAccess::READ); - - bool first = true; - if (f) { - //read the disk cache - while (!f->eof_reached()) { - String l = f->get_line().strip_edges(); - if (first) { - if (first_scan) { - // only use this on first scan, afterwards it gets ignored - // this is so on first reimport we synchronize versions, then - // we don't care until editor restart. This is for usability mainly so - // your workflow is not killed after changing a setting by forceful reimporting - // everything there is. - filesystem_settings_version_for_import = l.strip_edges(); - if (filesystem_settings_version_for_import != ResourceFormatImporter::get_singleton()->get_import_settings_hash()) { - revalidate_import_files = true; + { + Ref<FileAccess> f = FileAccess::open(fscache, FileAccess::READ); + + bool first = true; + if (f.is_valid()) { + //read the disk cache + while (!f->eof_reached()) { + String l = f->get_line().strip_edges(); + if (first) { + if (first_scan) { + // only use this on first scan, afterwards it gets ignored + // this is so on first reimport we synchronize versions, then + // we don't care until editor restart. This is for usability mainly so + // your workflow is not killed after changing a setting by forceful reimporting + // everything there is. + filesystem_settings_version_for_import = l.strip_edges(); + if (filesystem_settings_version_for_import != ResourceFormatImporter::get_singleton()->get_import_settings_hash()) { + revalidate_import_files = true; + } } + first = false; + continue; + } + if (l.is_empty()) { + continue; } - first = false; - continue; - } - if (l.is_empty()) { - continue; - } - if (l.begins_with("::")) { - Vector<String> split = l.split("::"); - ERR_CONTINUE(split.size() != 3); - String name = split[1]; + if (l.begins_with("::")) { + Vector<String> split = l.split("::"); + ERR_CONTINUE(split.size() != 3); + String name = split[1]; - cpath = name; + cpath = name; - } else { - Vector<String> split = l.split("::"); - ERR_CONTINUE(split.size() != 9); - String name = split[0]; - String file; - - file = name; - name = cpath.plus_file(name); - - FileCache fc; - fc.type = split[1]; - fc.uid = split[2].to_int(); - fc.modification_time = split[3].to_int(); - fc.import_modification_time = split[4].to_int(); - fc.import_valid = split[5].to_int() != 0; - fc.import_group_file = split[6].strip_edges(); - fc.script_class_name = split[7].get_slice("<>", 0); - fc.script_class_extends = split[7].get_slice("<>", 1); - fc.script_class_icon_path = split[7].get_slice("<>", 2); - - String deps = split[8].strip_edges(); - if (deps.length()) { - Vector<String> dp = deps.split("<>"); - for (int i = 0; i < dp.size(); i++) { - String path = dp[i]; - fc.deps.push_back(path); + } else { + Vector<String> split = l.split("::"); + ERR_CONTINUE(split.size() != 9); + String name = split[0]; + String file; + + file = name; + name = cpath.plus_file(name); + + FileCache fc; + fc.type = split[1]; + fc.uid = split[2].to_int(); + fc.modification_time = split[3].to_int(); + fc.import_modification_time = split[4].to_int(); + fc.import_valid = split[5].to_int() != 0; + fc.import_group_file = split[6].strip_edges(); + fc.script_class_name = split[7].get_slice("<>", 0); + fc.script_class_extends = split[7].get_slice("<>", 1); + fc.script_class_icon_path = split[7].get_slice("<>", 2); + + String deps = split[8].strip_edges(); + if (deps.length()) { + Vector<String> dp = deps.split("<>"); + for (int i = 0; i < dp.size(); i++) { + String path = dp[i]; + fc.deps.push_back(path); + } } - } - file_cache[name] = fc; + file_cache[name] = fc; + } } } - - f->close(); - memdelete(f); } String update_cache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update4"); if (FileAccess::exists(update_cache)) { { - FileAccessRef f2 = FileAccess::open(update_cache, FileAccess::READ); + Ref<FileAccess> f2 = FileAccess::open(update_cache, FileAccess::READ); String l = f2->get_line().strip_edges(); while (!l.is_empty()) { file_cache.erase(l); //erase cache for this, so it gets updated @@ -300,7 +299,7 @@ void EditorFileSystem::_scan_filesystem() { } } - DirAccessRef d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); d->remove(update_cache); //bye bye update cache } @@ -314,14 +313,12 @@ void EditorFileSystem::_scan_filesystem() { new_filesystem = memnew(EditorFileSystemDirectory); new_filesystem->parent = nullptr; - DirAccess *d = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES); d->change_dir("res://"); _scan_new_dir(new_filesystem, d, sp); file_cache.clear(); //clear caches, no longer needed - memdelete(d); - if (!first_scan) { //on the first scan this is done from the main thread after re-importing _save_filesystem_cache(); @@ -335,13 +332,11 @@ void EditorFileSystem::_save_filesystem_cache() { String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(CACHE_FILE_NAME); - FileAccess *f = FileAccess::open(fscache, FileAccess::WRITE); - ERR_FAIL_COND_MSG(!f, "Cannot create file '" + fscache + "'. Check user write permissions."); + Ref<FileAccess> f = FileAccess::open(fscache, FileAccess::WRITE); + ERR_FAIL_COND_MSG(f.is_null(), "Cannot create file '" + fscache + "'. Check user write permissions."); f->store_line(filesystem_settings_version_for_import); _save_filesystem_cache(filesystem, f); - f->close(); - memdelete(f); } void EditorFileSystem::_thread_func(void *_userdata) { @@ -364,9 +359,9 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo } Error err; - FileAccess *f = FileAccess::open(p_path + ".import", FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path + ".import", FileAccess::READ, &err); - if (!f) { //no import file, do reimport + if (f.is_null()) { //no import file, do reimport return true; } @@ -400,7 +395,6 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo break; } else if (err != OK) { ERR_PRINT("ResourceFormatImporter::load - '" + p_path + ".import:" + itos(lines) + "' error '" + error_text + "'."); - memdelete(f); return false; //parse error, try reimport manually (Avoid reimport loop on broken file) } @@ -431,8 +425,6 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo } } - memdelete(f); - if (importer_name == "keep") { return false; //keep mode, do not reimport } @@ -453,8 +445,8 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo // Read the md5's from a separate file (so the import parameters aren't dependent on the file version String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(p_path); - FileAccess *md5s = FileAccess::open(base_path + ".md5", FileAccess::READ, &err); - if (!md5s) { // No md5's stored for this resource + Ref<FileAccess> md5s = FileAccess::open(base_path + ".md5", FileAccess::READ, &err); + if (md5s.is_null()) { // No md5's stored for this resource return true; } @@ -472,7 +464,6 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo break; } else if (err != OK) { ERR_PRINT("ResourceFormatImporter::load - '" + p_path + ".import.md5:" + itos(lines) + "' error '" + error_text + "'."); - memdelete(md5s); return false; // parse error } if (!assign.is_empty()) { @@ -485,7 +476,6 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo } } } - memdelete(md5s); //imported files are gone, reimport for (const String &E : to_check) { @@ -742,7 +732,7 @@ EditorFileSystem::ScanProgress EditorFileSystem::ScanProgress::get_sub(int p_cur return sp; } -void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess *da, const ScanProgress &p_progress) { +void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAccess> &da, const ScanProgress &p_progress) { List<String> dirs; List<String> files; @@ -967,7 +957,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const //then scan files and directories and check what's different - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); Error ret = da->change_dir(cd); ERR_FAIL_COND_MSG(ret != OK, "Cannot change to '" + cd + "' folder."); @@ -998,10 +988,9 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const efd->parent = p_dir; efd->name = f; - DirAccess *d = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES); d->change_dir(cd.plus_file(f)); _scan_new_dir(efd, d, p_progress.get_sub(1, 1)); - memdelete(d); ItemAction ia; ia.action = ItemAction::ACTION_DIR_ADD; @@ -1137,7 +1126,7 @@ void EditorFileSystem::_delete_internal_files(String p_file) { if (FileAccess::exists(p_file + ".import")) { List<String> paths; ResourceFormatImporter::get_singleton()->get_internal_resource_path_list(p_file, &paths); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); for (const String &E : paths) { da->remove(E); } @@ -1282,7 +1271,7 @@ EditorFileSystemDirectory *EditorFileSystem::get_filesystem() { return filesystem; } -void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir, FileAccess *p_file) { +void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir, Ref<FileAccess> p_file) { if (!p_dir) { return; //none } @@ -1467,8 +1456,8 @@ EditorFileSystemDirectory *EditorFileSystem::get_filesystem_path(const String &p void EditorFileSystem::_save_late_updated_files() { //files that already existed, and were modified, need re-scanning for dependencies upon project restart. This is done via saving this special file String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update4"); - FileAccessRef f = FileAccess::open(fscache, FileAccess::WRITE); - ERR_FAIL_COND_MSG(!f, "Cannot create file '" + fscache + "'. Check user write permissions."); + Ref<FileAccess> f = FileAccess::open(fscache, FileAccess::WRITE); + ERR_FAIL_COND_MSG(f.is_null(), "Cannot create file '" + fscache + "'. Check user write permissions."); for (Set<String>::Element *E = late_update_files.front(); E; E = E->next()) { f->store_line(E->get()); } @@ -1713,78 +1702,78 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector for (const KeyValue<String, Map<StringName, Variant>> &E : source_file_options) { const String &file = E.key; String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(file); - FileAccessRef f = FileAccess::open(file + ".import", FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(!f, ERR_FILE_CANT_OPEN, "Cannot open import file '" + file + ".import'."); - - //write manually, as order matters ([remap] has to go first for performance). - f->store_line("[remap]"); - f->store_line(""); - f->store_line("importer=\"" + importer->get_importer_name() + "\""); - int version = importer->get_format_version(); - if (version > 0) { - f->store_line("importer_version=" + itos(version)); - } - if (!importer->get_resource_type().is_empty()) { - f->store_line("type=\"" + importer->get_resource_type() + "\""); - } - Vector<String> dest_paths; + { + Ref<FileAccess> f = FileAccess::open(file + ".import", FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, "Cannot open import file '" + file + ".import'."); + + //write manually, as order matters ([remap] has to go first for performance). + f->store_line("[remap]"); + f->store_line(""); + f->store_line("importer=\"" + importer->get_importer_name() + "\""); + int version = importer->get_format_version(); + if (version > 0) { + f->store_line("importer_version=" + itos(version)); + } + if (!importer->get_resource_type().is_empty()) { + f->store_line("type=\"" + importer->get_resource_type() + "\""); + } - if (err == OK) { - String path = base_path + "." + importer->get_save_extension(); - f->store_line("path=\"" + path + "\""); - dest_paths.push_back(path); - } - - f->store_line("group_file=" + Variant(p_group_file).get_construct_string()); - - if (err == OK) { - f->store_line("valid=true"); - } else { - f->store_line("valid=false"); - } - f->store_line("[deps]\n"); + if (err == OK) { + String path = base_path + "." + importer->get_save_extension(); + f->store_line("path=\"" + path + "\""); + dest_paths.push_back(path); + } - f->store_line(""); + f->store_line("group_file=" + Variant(p_group_file).get_construct_string()); - f->store_line("source_file=" + Variant(file).get_construct_string()); - if (dest_paths.size()) { - Array dp; - for (int i = 0; i < dest_paths.size(); i++) { - dp.push_back(dest_paths[i]); + if (err == OK) { + f->store_line("valid=true"); + } else { + f->store_line("valid=false"); } - f->store_line("dest_files=" + Variant(dp).get_construct_string() + "\n"); - } - f->store_line("[params]"); - f->store_line(""); + f->store_line("[deps]\n"); - //store options in provided order, to avoid file changing. Order is also important because first match is accepted first. + f->store_line(""); - List<ResourceImporter::ImportOption> options; - importer->get_import_options(file, &options); - //set default values - for (const ResourceImporter::ImportOption &F : options) { - String base = F.option.name; - Variant v = F.default_value; - if (source_file_options[file].has(base)) { - v = source_file_options[file][base]; + f->store_line("source_file=" + Variant(file).get_construct_string()); + if (dest_paths.size()) { + Array dp; + for (int i = 0; i < dest_paths.size(); i++) { + dp.push_back(dest_paths[i]); + } + f->store_line("dest_files=" + Variant(dp).get_construct_string() + "\n"); + } + f->store_line("[params]"); + f->store_line(""); + + //store options in provided order, to avoid file changing. Order is also important because first match is accepted first. + + List<ResourceImporter::ImportOption> options; + importer->get_import_options(file, &options); + //set default values + for (const ResourceImporter::ImportOption &F : options) { + String base = F.option.name; + Variant v = F.default_value; + if (source_file_options[file].has(base)) { + v = source_file_options[file][base]; + } + String value; + VariantWriter::write_to_string(v, value); + f->store_line(base + "=" + value); } - String value; - VariantWriter::write_to_string(v, value); - f->store_line(base + "=" + value); } - f->close(); - // Store the md5's of the various files. These are stored separately so that the .import files can be version controlled. - FileAccessRef md5s = FileAccess::open(base_path + ".md5", FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(!md5s, ERR_FILE_CANT_OPEN, "Cannot open MD5 file '" + base_path + ".md5'."); + { + Ref<FileAccess> md5s = FileAccess::open(base_path + ".md5", FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(md5s.is_null(), ERR_FILE_CANT_OPEN, "Cannot open MD5 file '" + base_path + ".md5'."); - md5s->store_line("source_md5=\"" + FileAccess::get_md5(file) + "\""); - if (dest_paths.size()) { - md5s->store_line("dest_md5=\"" + FileAccess::get_multiple_md5(dest_paths) + "\"\n"); + md5s->store_line("source_md5=\"" + FileAccess::get_md5(file) + "\""); + if (dest_paths.size()) { + md5s->store_line("dest_md5=\"" + FileAccess::get_multiple_md5(dest_paths) + "\"\n"); + } } - md5s->close(); EditorFileSystemDirectory *fs = nullptr; int cpos = -1; @@ -1930,106 +1919,104 @@ void EditorFileSystem::_reimport_file(const String &p_file, const Map<StringName //as import is complete, save the .import file - FileAccess *f = FileAccess::open(p_file + ".import", FileAccess::WRITE); - ERR_FAIL_COND_MSG(!f, "Cannot open file from path '" + p_file + ".import'."); - - //write manually, as order matters ([remap] has to go first for performance). - f->store_line("[remap]"); - f->store_line(""); - f->store_line("importer=\"" + importer->get_importer_name() + "\""); - int version = importer->get_format_version(); - if (version > 0) { - f->store_line("importer_version=" + itos(version)); - } - if (!importer->get_resource_type().is_empty()) { - f->store_line("type=\"" + importer->get_resource_type() + "\""); - } - - if (uid == ResourceUID::INVALID_ID) { - uid = ResourceUID::get_singleton()->create_id(); - } + Vector<String> dest_paths; + { + Ref<FileAccess> f = FileAccess::open(p_file + ".import", FileAccess::WRITE); + ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file from path '" + p_file + ".import'."); - f->store_line("uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\""); //store in readable format + //write manually, as order matters ([remap] has to go first for performance). + f->store_line("[remap]"); + f->store_line(""); + f->store_line("importer=\"" + importer->get_importer_name() + "\""); + int version = importer->get_format_version(); + if (version > 0) { + f->store_line("importer_version=" + itos(version)); + } + if (!importer->get_resource_type().is_empty()) { + f->store_line("type=\"" + importer->get_resource_type() + "\""); + } - Vector<String> dest_paths; + if (uid == ResourceUID::INVALID_ID) { + uid = ResourceUID::get_singleton()->create_id(); + } - if (err == OK) { - if (importer->get_save_extension().is_empty()) { - //no path - } else if (import_variants.size()) { - //import with variants - for (const String &E : import_variants) { - String path = base_path.c_escape() + "." + E + "." + importer->get_save_extension(); + f->store_line("uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\""); //store in readable format - f->store_line("path." + E + "=\"" + path + "\""); + if (err == OK) { + if (importer->get_save_extension().is_empty()) { + //no path + } else if (import_variants.size()) { + //import with variants + for (const String &E : import_variants) { + String path = base_path.c_escape() + "." + E + "." + importer->get_save_extension(); + + f->store_line("path." + E + "=\"" + path + "\""); + dest_paths.push_back(path); + } + } else { + String path = base_path + "." + importer->get_save_extension(); + f->store_line("path=\"" + path + "\""); dest_paths.push_back(path); } + } else { - String path = base_path + "." + importer->get_save_extension(); - f->store_line("path=\"" + path + "\""); - dest_paths.push_back(path); + f->store_line("valid=false"); } - } else { - f->store_line("valid=false"); - } + if (metadata != Variant()) { + f->store_line("metadata=" + metadata.get_construct_string()); + } - if (metadata != Variant()) { - f->store_line("metadata=" + metadata.get_construct_string()); - } + f->store_line(""); - f->store_line(""); + f->store_line("[deps]\n"); - f->store_line("[deps]\n"); + if (gen_files.size()) { + Array genf; + for (const String &E : gen_files) { + genf.push_back(E); + dest_paths.push_back(E); + } - if (gen_files.size()) { - Array genf; - for (const String &E : gen_files) { - genf.push_back(E); - dest_paths.push_back(E); + String value; + VariantWriter::write_to_string(genf, value); + f->store_line("files=" + value); + f->store_line(""); } - String value; - VariantWriter::write_to_string(genf, value); - f->store_line("files=" + value); - f->store_line(""); - } - - f->store_line("source_file=" + Variant(p_file).get_construct_string()); + f->store_line("source_file=" + Variant(p_file).get_construct_string()); - if (dest_paths.size()) { - Array dp; - for (int i = 0; i < dest_paths.size(); i++) { - dp.push_back(dest_paths[i]); + if (dest_paths.size()) { + Array dp; + for (int i = 0; i < dest_paths.size(); i++) { + dp.push_back(dest_paths[i]); + } + f->store_line("dest_files=" + Variant(dp).get_construct_string() + "\n"); } - f->store_line("dest_files=" + Variant(dp).get_construct_string() + "\n"); - } - f->store_line("[params]"); - f->store_line(""); + f->store_line("[params]"); + f->store_line(""); - //store options in provided order, to avoid file changing. Order is also important because first match is accepted first. + //store options in provided order, to avoid file changing. Order is also important because first match is accepted first. - for (const ResourceImporter::ImportOption &E : opts) { - String base = E.option.name; - String value; - VariantWriter::write_to_string(params[base], value); - f->store_line(base + "=" + value); + for (const ResourceImporter::ImportOption &E : opts) { + String base = E.option.name; + String value; + VariantWriter::write_to_string(params[base], value); + f->store_line(base + "=" + value); + } } - f->close(); - memdelete(f); - // Store the md5's of the various files. These are stored separately so that the .import files can be version controlled. - FileAccess *md5s = FileAccess::open(base_path + ".md5", FileAccess::WRITE); - ERR_FAIL_COND_MSG(!md5s, "Cannot open MD5 file '" + base_path + ".md5'."); + { + Ref<FileAccess> md5s = FileAccess::open(base_path + ".md5", FileAccess::WRITE); + ERR_FAIL_COND_MSG(md5s.is_null(), "Cannot open MD5 file '" + base_path + ".md5'."); - md5s->store_line("source_md5=\"" + FileAccess::get_md5(p_file) + "\""); - if (dest_paths.size()) { - md5s->store_line("dest_md5=\"" + FileAccess::get_multiple_md5(dest_paths) + "\"\n"); + md5s->store_line("source_md5=\"" + FileAccess::get_md5(p_file) + "\""); + if (dest_paths.size()) { + md5s->store_line("dest_md5=\"" + FileAccess::get_multiple_md5(dest_paths) + "\"\n"); + } } - md5s->close(); - memdelete(md5s); //update modified times, to avoid reimport fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file); @@ -2342,14 +2329,14 @@ bool EditorFileSystem::_scan_extensions() { String extension_list_config_file = NativeExtension::get_extension_list_config_file(); if (extensions.size()) { if (extensions_added.size() || extensions_removed.size()) { //extensions were added or removed - FileAccessRef f = FileAccess::open(extension_list_config_file, FileAccess::WRITE); + Ref<FileAccess> f = FileAccess::open(extension_list_config_file, FileAccess::WRITE); for (const String &E : extensions) { f->store_line(E); } } } else { if (loaded_extensions.size() || FileAccess::exists(extension_list_config_file)) { //extensions were removed - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); da->remove(extension_list_config_file); } } @@ -2438,7 +2425,7 @@ EditorFileSystem::EditorFileSystem() { new_filesystem = nullptr; // This should probably also work on Unix and use the string it returns for FAT32 or exFAT - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); using_fat32_or_exfat = (da->get_filesystem_type() == "FAT32" || da->get_filesystem_type() == "exFAT"); scan_total = 0; diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 05b55d4e1b..81811d2eb0 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -213,7 +213,7 @@ class EditorFileSystem : public Node { }; void _save_filesystem_cache(); - void _save_filesystem_cache(EditorFileSystemDirectory *p_dir, FileAccess *p_file); + void _save_filesystem_cache(EditorFileSystemDirectory *p_dir, Ref<FileAccess> p_file); bool _find_file(const String &p_file, EditorFileSystemDirectory **r_d, int &r_file_pos) const; @@ -225,7 +225,7 @@ class EditorFileSystem : public Node { Set<String> valid_extensions; Set<String> import_extensions; - void _scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess *da, const ScanProgress &p_progress); + void _scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAccess> &da, const ScanProgress &p_progress); Thread thread_sources; bool scanning_changes = false; diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp index 8d6ebd1154..548f05217e 100644 --- a/editor/editor_folding.cpp +++ b/editor/editor_folding.cpp @@ -131,7 +131,7 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path) { ERR_FAIL_NULL(p_scene); - FileAccessRef file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); + Ref<FileAccess> file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); if (!file_check->file_exists(p_path)) { //This can happen when creating scene from FilesystemDock. It has path, but no file. return; } diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index 66fe3c4838..3e18499b97 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -218,7 +218,7 @@ Ref<FontData> load_cached_internal_font(const uint8_t *p_data, size_t p_size, Te } void editor_register_fonts(Ref<Theme> p_theme) { - DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); /* Custom font */ diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index d5085942c3..30f3748fa4 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -2682,7 +2682,7 @@ void EditorInspector::update_tree() { { const int dot = name_override.find("."); if (dot != -1) { - feature_tag = name_override.right(dot); + feature_tag = name_override.substr(dot); name_override = name_override.substr(0, dot); } } diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index ee2d72c5b0..8d45f90ed6 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -168,7 +168,7 @@ void EditorLog::_copy_request() { String text = log->get_selected_text(); if (text.is_empty()) { - text = log->get_text(); + text = log->get_parsed_text(); } if (!text.is_empty()) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index a80c7853f5..6fe6309eed 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -416,7 +416,7 @@ void EditorNode::_version_control_menu_option(int p_idx) { void EditorNode::_update_title() { const String appname = ProjectSettings::get_singleton()->get("application/config/name"); - String title = (appname.is_empty() ? "Unnamed Project" : appname) + String(" - ") + VERSION_NAME; + String title = (appname.is_empty() ? TTR("Unnamed Project") : appname) + String(" - ") + VERSION_NAME; const String edited = editor_data.get_edited_scene_root() ? editor_data.get_edited_scene_root()->get_scene_file_path() : String(); if (!edited.is_empty()) { // Display the edited scene name before the program name so that it can be seen in the OS task bar. @@ -935,7 +935,7 @@ void EditorNode::_fs_changed() { } if (export_preset.is_null()) { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (da->file_exists("res://export_presets.cfg")) { export_error = vformat( "Invalid export preset name: %s.\nThe following presets were detected in this project's `export_presets.cfg`:\n\n", @@ -1058,7 +1058,7 @@ void EditorNode::_scan_external_changes() { // Check if any edited scene has changed. for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (editor_data.get_scene_path(i) == "" || !da->file_exists(editor_data.get_scene_path(i))) { continue; } @@ -5478,7 +5478,7 @@ void EditorNode::_global_menu_new_window(const Variant &p_tag) { } } -void EditorNode::_dropped_files(const Vector<String> &p_files, int p_screen) { +void EditorNode::_dropped_files(const Vector<String> &p_files) { String to_path = ProjectSettings::get_singleton()->globalize_path(FileSystemDock::get_singleton()->get_selected_path()); _add_dropped_files_recursive(p_files, to_path); @@ -5487,7 +5487,7 @@ void EditorNode::_dropped_files(const Vector<String> &p_files, int p_screen) { } void EditorNode::_add_dropped_files_recursive(const Vector<String> &p_files, String to_path) { - DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); for (int i = 0; i < p_files.size(); i++) { String from = p_files[i]; @@ -5496,7 +5496,7 @@ void EditorNode::_add_dropped_files_recursive(const Vector<String> &p_files, Str if (dir->dir_exists(from)) { Vector<String> sub_files; - DirAccessRef sub_dir = DirAccess::open(from); + Ref<DirAccess> sub_dir = DirAccess::open(from); sub_dir->list_dir_begin(); String next_file = sub_dir->get_next(); @@ -5997,18 +5997,22 @@ EditorNode::EditorNode() { import_scene.instantiate(); ResourceFormatImporter::get_singleton()->add_importer(import_scene); + Ref<ResourceImporterScene> import_animation; + import_animation = Ref<ResourceImporterScene>(memnew(ResourceImporterScene(true))); + ResourceFormatImporter::get_singleton()->add_importer(import_animation); + { Ref<EditorSceneFormatImporterCollada> import_collada; import_collada.instantiate(); - import_scene->add_importer(import_collada); + ResourceImporterScene::add_importer(import_collada); Ref<EditorOBJImporter> import_obj2; import_obj2.instantiate(); - import_scene->add_importer(import_obj2); + ResourceImporterScene::add_importer(import_obj2); Ref<EditorSceneFormatImporterESCN> import_escn; import_escn.instantiate(); - import_scene->add_importer(import_escn); + ResourceImporterScene::add_importer(import_escn); } Ref<ResourceImporterBitMap> import_bitmap; @@ -7245,6 +7249,7 @@ EditorNode::EditorNode() { EditorNode::~EditorNode() { EditorInspector::cleanup_plugins(); EditorTranslationParser::get_singleton()->clean_parsers(); + ResourceImporterScene::clean_up_importer_plugins(); remove_print_handler(&print_handler); EditorHelp::cleanup_doc(); diff --git a/editor/editor_node.h b/editor/editor_node.h index 685714cb47..c6c1f09938 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -575,7 +575,7 @@ private: void _open_recent_scene(int p_idx); void _global_menu_scene(const Variant &p_tag); void _global_menu_new_window(const Variant &p_tag); - void _dropped_files(const Vector<String> &p_files, int p_screen); + void _dropped_files(const Vector<String> &p_files); void _add_dropped_files_recursive(const Vector<String> &p_files, String to_path); void _update_from_settings(); diff --git a/editor/editor_paths.cpp b/editor/editor_paths.cpp index 7b454055e0..a5c2fe093c 100644 --- a/editor/editor_paths.cpp +++ b/editor/editor_paths.cpp @@ -97,7 +97,7 @@ EditorPaths::EditorPaths() { exe_path = exe_path.plus_file("../../..").simplify_path(); } { - DirAccessRef d = DirAccess::create_for_path(exe_path); + Ref<DirAccess> d = DirAccess::create_for_path(exe_path); if (d->file_exists(exe_path + "/._sc_")) { self_contained = true; @@ -141,7 +141,7 @@ EditorPaths::EditorPaths() { // Validate or create each dir and its relevant subdirectories. - DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); // Data dir. { @@ -197,7 +197,7 @@ EditorPaths::EditorPaths() { // Nothing to create, use shared editor data dir for shader cache. Engine::get_singleton()->set_shader_cache_path(data_dir); } else { - DirAccessRef dir_res = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> dir_res = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (dir_res->change_dir(project_data_dir) != OK) { dir_res->make_dir_recursive(project_data_dir); if (dir_res->change_dir(project_data_dir) != OK) { @@ -210,10 +210,9 @@ EditorPaths::EditorPaths() { String project_data_gdignore_file_path = project_data_dir.plus_file(".gdignore"); if (!FileAccess::exists(project_data_gdignore_file_path)) { // Add an empty .gdignore file to avoid scan. - FileAccessRef f = FileAccess::open(project_data_gdignore_file_path, FileAccess::WRITE); - if (f) { + Ref<FileAccess> f = FileAccess::open(project_data_gdignore_file_path, FileAccess::WRITE); + if (f.is_valid()) { f->store_line(""); - f->close(); } else { ERR_PRINT("Failed to create file " + project_data_gdignore_file_path); } diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 5166200ee3..6596c245bc 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -763,20 +763,20 @@ void EditorPlugin::remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_p void EditorPlugin::add_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer, bool p_first_priority) { ERR_FAIL_COND(!p_importer.is_valid()); - ResourceImporterScene::get_singleton()->add_importer(p_importer, p_first_priority); + ResourceImporterScene::add_importer(p_importer, p_first_priority); } void EditorPlugin::remove_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer) { ERR_FAIL_COND(!p_importer.is_valid()); - ResourceImporterScene::get_singleton()->remove_importer(p_importer); + ResourceImporterScene::remove_importer(p_importer); } void EditorPlugin::add_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin, bool p_first_priority) { - ResourceImporterScene::get_singleton()->add_post_importer_plugin(p_plugin, p_first_priority); + ResourceImporterScene::add_post_importer_plugin(p_plugin, p_first_priority); } void EditorPlugin::remove_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) { - ResourceImporterScene::get_singleton()->remove_post_importer_plugin(p_plugin); + ResourceImporterScene::remove_post_importer_plugin(p_plugin); } int find(const PackedStringArray &a, const String &v) { diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp index b6f48c7536..b728ce64c9 100644 --- a/editor/editor_plugin_settings.cpp +++ b/editor/editor_plugin_settings.cpp @@ -161,7 +161,7 @@ void EditorPluginSettings::_cell_button_pressed(Object *p_item, int p_column, in } Vector<String> EditorPluginSettings::_get_plugins(const String &p_dir) { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); Error err = da->change_dir(p_dir); if (err != OK) { return Vector<String>(); diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index 2bc92427e5..8541918e88 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -199,14 +199,12 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref< if (has_small_texture) { ResourceSaver::save(cache_base + "_small.png", r_small_texture); } - FileAccess *f = FileAccess::open(cache_base + ".txt", FileAccess::WRITE); - ERR_FAIL_COND_MSG(!f, "Cannot create file '" + cache_base + ".txt'. Check user write permissions."); + Ref<FileAccess> f = FileAccess::open(cache_base + ".txt", FileAccess::WRITE); + ERR_FAIL_COND_MSG(f.is_null(), "Cannot create file '" + cache_base + ".txt'. Check user write permissions."); f->store_line(itos(thumbnail_size)); f->store_line(itos(has_small_texture)); f->store_line(itos(FileAccess::get_modified_time(p_item.path))); f->store_line(FileAccess::get_md5(p_item.path)); - f->close(); - memdelete(f); } } } @@ -251,8 +249,8 @@ void EditorResourcePreview::_iterate() { //does not have it, try to load a cached thumbnail String file = cache_base + ".txt"; - FileAccess *f = FileAccess::open(file, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(file, FileAccess::READ); + if (f.is_null()) { // No cache found, generate _generate_preview(texture, small_texture, item, cache_base); } else { @@ -265,33 +263,31 @@ void EditorResourcePreview::_iterate() { if (tsize != thumbnail_size) { cache_valid = false; - memdelete(f); + f.unref(); } else if (last_modtime != modtime) { String last_md5 = f->get_line(); String md5 = FileAccess::get_md5(item.path); - memdelete(f); + f.unref(); if (last_md5 != md5) { cache_valid = false; - } else { //update modified time - f = FileAccess::open(file, FileAccess::WRITE); - if (!f) { + Ref<FileAccess> f2 = FileAccess::open(file, FileAccess::WRITE); + if (f2.is_null()) { // Not returning as this would leave the thread hanging and would require // some proper cleanup/disabling of resource preview generation. ERR_PRINT("Cannot create file '" + file + "'. Check user write permissions."); } else { - f->store_line(itos(thumbnail_size)); - f->store_line(itos(has_small_texture)); - f->store_line(itos(modtime)); - f->store_line(md5); - memdelete(f); + f2->store_line(itos(thumbnail_size)); + f2->store_line(itos(has_small_texture)); + f2->store_line(itos(modtime)); + f2->store_line(md5); } } } else { - memdelete(f); + f.unref(); } if (cache_valid) { diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 4ddc66ed98..48f04694aa 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -722,7 +722,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { /* Extra config */ - EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "project_manager/sorting_order", 0, "Name,Path,Last Edited") + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "project_manager/sorting_order", 0, "Last Edited,Name,Path") if (p_extra_config.is_valid()) { if (p_extra_config->has_section("init_projects") && p_extra_config->has_section_key("init_projects", "list")) { @@ -842,7 +842,7 @@ void EditorSettings::create() { if (EditorPaths::get_singleton()->are_paths_valid()) { // Validate editor config file. - DirAccessRef dir = DirAccess::open(EditorPaths::get_singleton()->get_config_dir()); + Ref<DirAccess> dir = DirAccess::open(EditorPaths::get_singleton()->get_config_dir()); String config_file_name = "editor_settings-" + itos(VERSION_MAJOR) + ".tres"; config_file_path = EditorPaths::get_singleton()->get_config_dir().plus_file(config_file_name); if (!dir->file_exists(config_file_name)) { @@ -1151,12 +1151,11 @@ void EditorSettings::set_favorites(const Vector<String> &p_favorites) { } else { favorites_file = get_project_settings_dir().plus_file("favorites"); } - FileAccess *f = FileAccess::open(favorites_file, FileAccess::WRITE); - if (f) { + Ref<FileAccess> f = FileAccess::open(favorites_file, FileAccess::WRITE); + if (f.is_valid()) { for (int i = 0; i < favorites.size(); i++) { f->store_line(favorites[i]); } - memdelete(f); } } @@ -1172,12 +1171,11 @@ void EditorSettings::set_recent_dirs(const Vector<String> &p_recent_dirs) { } else { recent_dirs_file = get_project_settings_dir().plus_file("recent_dirs"); } - FileAccess *f = FileAccess::open(recent_dirs_file, FileAccess::WRITE); - if (f) { + Ref<FileAccess> f = FileAccess::open(recent_dirs_file, FileAccess::WRITE); + if (f.is_valid()) { for (int i = 0; i < recent_dirs.size(); i++) { f->store_line(recent_dirs[i]); } - memdelete(f); } } @@ -1195,24 +1193,22 @@ void EditorSettings::load_favorites_and_recent_dirs() { favorites_file = get_project_settings_dir().plus_file("favorites"); recent_dirs_file = get_project_settings_dir().plus_file("recent_dirs"); } - FileAccess *f = FileAccess::open(favorites_file, FileAccess::READ); - if (f) { + Ref<FileAccess> f = FileAccess::open(favorites_file, FileAccess::READ); + if (f.is_valid()) { String line = f->get_line().strip_edges(); while (!line.is_empty()) { favorites.push_back(line); line = f->get_line().strip_edges(); } - memdelete(f); } f = FileAccess::open(recent_dirs_file, FileAccess::READ); - if (f) { + if (f.is_valid()) { String line = f->get_line().strip_edges(); while (!line.is_empty()) { recent_dirs.push_back(line); line = f->get_line().strip_edges(); } - memdelete(f); } } @@ -1227,8 +1223,8 @@ bool EditorSettings::is_dark_theme() { void EditorSettings::list_text_editor_themes() { String themes = "Default,Godot 2,Custom"; - DirAccessRef d = DirAccess::open(get_text_editor_themes_dir()); - if (d) { + Ref<DirAccess> d = DirAccess::open(get_text_editor_themes_dir()); + if (d.is_valid()) { List<String> custom_themes; d->list_dir_begin(); String file = d->get_next(); @@ -1293,8 +1289,8 @@ bool EditorSettings::import_text_editor_theme(String p_file) { return false; } - DirAccessRef d = DirAccess::open(get_text_editor_themes_dir()); - if (d) { + Ref<DirAccess> d = DirAccess::open(get_text_editor_themes_dir()); + if (d.is_valid()) { d->copy(p_file, get_text_editor_themes_dir().plus_file(p_file.get_file())); return true; } @@ -1345,8 +1341,8 @@ Vector<String> EditorSettings::get_script_templates(const String &p_extension, c if (!p_custom_path.is_empty()) { template_dir = p_custom_path; } - DirAccessRef d = DirAccess::open(template_dir); - if (d) { + Ref<DirAccess> d = DirAccess::open(template_dir); + if (d.is_valid()) { d->list_dir_begin(); String file = d->get_next(); while (!file.is_empty()) { diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 1fea759a90..f4082746d8 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1220,10 +1220,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_icon("breakpoint", "CodeEdit", theme->get_icon(SNAME("Breakpoint"), SNAME("EditorIcons"))); theme->set_constant("line_spacing", "CodeEdit", EDITOR_GET("text_editor/appearance/whitespace/line_spacing")); - // H/VSplitContainer - theme->set_stylebox("bg", "VSplitContainer", make_stylebox(theme->get_icon(SNAME("GuiVsplitBg"), SNAME("EditorIcons")), 1, 1, 1, 1)); - theme->set_stylebox("bg", "HSplitContainer", make_stylebox(theme->get_icon(SNAME("GuiHsplitBg"), SNAME("EditorIcons")), 1, 1, 1, 1)); - theme->set_icon("grabber", "VSplitContainer", theme->get_icon(SNAME("GuiVsplitter"), SNAME("EditorIcons"))); theme->set_icon("grabber", "HSplitContainer", theme->get_icon(SNAME("GuiHsplitter"), SNAME("EditorIcons"))); diff --git a/editor/editor_translation.cpp b/editor/editor_translation.cpp index f64adcf0a1..b08e5807e7 100644 --- a/editor/editor_translation.cpp +++ b/editor/editor_translation.cpp @@ -59,7 +59,8 @@ void load_editor_translations(const String &p_locale) { int ret = Compression::decompress(data.ptrw(), etl->uncomp_size, etl->data, etl->comp_size, Compression::MODE_DEFLATE); ERR_FAIL_COND_MSG(ret == -1, "Compressed file is corrupt."); - FileAccessMemory *fa = memnew(FileAccessMemory); + Ref<FileAccessMemory> fa; + fa.instantiate(); fa->open_custom(data.ptr(), data.size()); Ref<Translation> tr = TranslationLoaderPO::load_translation(fa); @@ -84,7 +85,8 @@ void load_doc_translations(const String &p_locale) { int ret = Compression::decompress(data.ptrw(), dtl->uncomp_size, dtl->data, dtl->comp_size, Compression::MODE_DEFLATE); ERR_FAIL_COND_MSG(ret == -1, "Compressed file is corrupt."); - FileAccessMemory *fa = memnew(FileAccessMemory); + Ref<FileAccessMemory> fa; + fa.instantiate(); fa->open_custom(data.ptr(), data.size()); Ref<Translation> tr = TranslationLoaderPO::load_translation(fa); diff --git a/editor/editor_vcs_interface.cpp b/editor/editor_vcs_interface.cpp index 0954779300..3f2012cc16 100644 --- a/editor/editor_vcs_interface.cpp +++ b/editor/editor_vcs_interface.cpp @@ -168,21 +168,19 @@ void EditorVCSInterface::set_singleton(EditorVCSInterface *p_singleton) { void EditorVCSInterface::create_vcs_metadata_files(VCSMetadata p_vcs_metadata_type, String &p_dir) { if (p_vcs_metadata_type == VCSMetadata::GIT) { - FileAccess *f = FileAccess::open(p_dir.plus_file(".gitignore"), FileAccess::WRITE); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_dir.plus_file(".gitignore"), FileAccess::WRITE); + if (f.is_null()) { ERR_FAIL_MSG(TTR("Couldn't create .gitignore in project path.")); } else { f->store_line("# Godot 4+ specific ignores"); f->store_line(".godot/"); - memdelete(f); } f = FileAccess::open(p_dir.plus_file(".gitattributes"), FileAccess::WRITE); - if (!f) { + if (f.is_null()) { ERR_FAIL_MSG(TTR("Couldn't create .gitattributes in project path.")); } else { f->store_line("# Normalize EOL for all files that Git considers text files."); f->store_line("* text=auto eol=lf"); - memdelete(f); } } } diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index f93c2df13d..b34b08b5de 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -44,7 +44,7 @@ void ExportTemplateManager::_update_template_status() { // Fetch installed templates from the file system. - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); const String &templates_dir = EditorSettings::get_singleton()->get_templates_dir(); Error err = da->change_dir(templates_dir); @@ -194,7 +194,7 @@ void ExportTemplateManager::_download_template_completed(int p_status, int p_cod bool ret = _install_file_selected(path, true); if (ret) { // Clean up downloaded file. - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); Error err = da->remove(path); if (err != OK) { EditorNode::get_singleton()->add_io_error(TTR("Cannot remove temporary file:") + "\n" + path + "\n"); @@ -374,10 +374,7 @@ void ExportTemplateManager::_install_file() { } bool ExportTemplateManager::_install_file_selected(const String &p_file, bool p_skip_progress) { - // unzClose() will take care of closing the file stored in the unzFile, - // so we don't need to `memdelete(fa)` in this method. - FileAccess *fa = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&fa); + zlib_filefunc_def io = zipio_create_io(); unzFile pkg = unzOpen2(p_file.utf8().get_data(), &io); if (!pkg) { @@ -407,9 +404,7 @@ bool ExportTemplateManager::_install_file_selected(const String &p_file, bool p_ // Read. unzOpenCurrentFile(pkg); ret = unzReadCurrentFile(pkg, data.ptrw(), data.size()); - if (ret != UNZ_OK) { - break; - } + ERR_BREAK_MSG(ret < 0, vformat("An error occurred while attempting to read from file: %s. This file will not be used.", file)); unzCloseCurrentFile(pkg); String data_str; @@ -441,7 +436,7 @@ bool ExportTemplateManager::_install_file_selected(const String &p_file, bool p_ return false; } - DirAccessRef d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String template_path = EditorSettings::get_singleton()->get_templates_dir().plus_file(version); Error err = d->make_dir_recursive(template_path); if (err != OK) { @@ -481,9 +476,7 @@ bool ExportTemplateManager::_install_file_selected(const String &p_file, bool p_ // Read unzOpenCurrentFile(pkg); ret = unzReadCurrentFile(pkg, data.ptrw(), data.size()); - if (ret != UNZ_OK) { - break; - } + ERR_BREAK_MSG(ret < 0, vformat("An error occurred while attempting to read from file: %s. This file will not be used.", file)); unzCloseCurrentFile(pkg); String base_dir = file_path.get_base_dir().trim_suffix("/"); @@ -492,8 +485,8 @@ bool ExportTemplateManager::_install_file_selected(const String &p_file, bool p_ base_dir = base_dir.substr(contents_dir.length(), file_path.length()).trim_prefix("/"); file = base_dir.plus_file(file); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_CONTINUE(!da); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_CONTINUE(da.is_null()); String output_dir = template_path.plus_file(base_dir); @@ -508,16 +501,16 @@ bool ExportTemplateManager::_install_file_selected(const String &p_file, bool p_ } String to_write = template_path.plus_file(file); - FileAccessRef f = FileAccess::open(to_write, FileAccess::WRITE); + Ref<FileAccess> f = FileAccess::open(to_write, FileAccess::WRITE); - if (!f) { + if (f.is_null()) { ret = unzGoToNextFile(pkg); fc++; ERR_CONTINUE_MSG(true, "Can't open file from path '" + String(to_write) + "'."); } f->store_buffer(data.ptr(), data.size()); - + f.unref(); // close file. #ifndef WINDOWS_ENABLED FileAccess::set_unix_permissions(to_write, (info.external_fa >> 16) & 0x01FF); #endif @@ -542,7 +535,7 @@ void ExportTemplateManager::_uninstall_template(const String &p_version) { } void ExportTemplateManager::_uninstall_template_confirmed() { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); const String &templates_dir = EditorSettings::get_singleton()->get_templates_dir(); Error err = da->change_dir(templates_dir); @@ -656,17 +649,16 @@ Error ExportTemplateManager::install_android_template_from_file(const String &p_ // To support custom Android builds, we install the Java source code and buildsystem // from android_source.zip to the project's res://android folder. - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + ERR_FAIL_COND_V(da.is_null(), ERR_CANT_CREATE); // Make res://android dir (if it does not exist). da->make_dir("android"); { // Add version, to ensure building won't work if template and Godot version don't match. - FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::WRITE); - ERR_FAIL_COND_V(!f, ERR_CANT_CREATE); + Ref<FileAccess> f = FileAccess::open("res://android/.build_version", FileAccess::WRITE); + ERR_FAIL_COND_V(f.is_null(), ERR_CANT_CREATE); f->store_line(VERSION_FULL_CONFIG); - f->close(); } // Create the android plugins directory. @@ -677,16 +669,14 @@ Error ExportTemplateManager::install_android_template_from_file(const String &p_ ERR_FAIL_COND_V(err != OK, err); { // Add an empty .gdignore file to avoid scan. - FileAccessRef f = FileAccess::open("res://android/build/.gdignore", FileAccess::WRITE); - ERR_FAIL_COND_V(!f, ERR_CANT_CREATE); + Ref<FileAccess> f = FileAccess::open("res://android/build/.gdignore", FileAccess::WRITE); + ERR_FAIL_COND_V(f.is_null(), ERR_CANT_CREATE); f->store_line(""); - f->close(); } // Uncompress source template. - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + zlib_filefunc_def io = zipio_create_io(); unzFile pkg = unzOpen2(p_file.utf8().get_data(), &io); ERR_FAIL_COND_V_MSG(!pkg, ERR_CANT_OPEN, "Android sources not in ZIP format."); @@ -731,10 +721,10 @@ Error ExportTemplateManager::install_android_template_from_file(const String &p_ } String to_write = String("res://android/build").plus_file(path); - FileAccess *f = FileAccess::open(to_write, FileAccess::WRITE); - if (f) { + Ref<FileAccess> f = FileAccess::open(to_write, FileAccess::WRITE); + if (f.is_valid()) { f->store_buffer(data.ptr(), data.size()); - memdelete(f); + f.unref(); // close file. #ifndef WINDOWS_ENABLED FileAccess::set_unix_permissions(to_write, (info.external_fa >> 16) & 0x01FF); #endif diff --git a/editor/fileserver/editor_file_server.cpp b/editor/fileserver/editor_file_server.cpp index 30dc9180e3..46fb767c00 100644 --- a/editor/fileserver/editor_file_server.cpp +++ b/editor/fileserver/editor_file_server.cpp @@ -46,7 +46,6 @@ void EditorFileServer::_close_client(ClientData *cd) { cd->efs->to_wait.insert(cd->thread); } while (cd->files.size()) { - memdelete(cd->files.front()->get()); cd->files.erase(cd->files.front()); } memdelete(cd); @@ -181,8 +180,8 @@ void EditorFileServer::_subthread_start(void *s) { break; } - FileAccess *fa = FileAccess::open(s2, FileAccess::READ); - if (!fa) { + Ref<FileAccess> fa = FileAccess::open(s2, FileAccess::READ); + if (fa.is_null()) { //not found, continue encode_uint32(id, buf4); cd->connection->put_data(buf4, 4); @@ -249,7 +248,6 @@ void EditorFileServer::_subthread_start(void *s) { case FileAccessNetwork::COMMAND_CLOSE: { print_verbose("CLOSED"); ERR_CONTINUE(!cd->files.has(id)); - memdelete(cd->files[id]); cd->files.erase(id); } break; } diff --git a/editor/fileserver/editor_file_server.h b/editor/fileserver/editor_file_server.h index a1bb7ecf4e..ccebd1465d 100644 --- a/editor/fileserver/editor_file_server.h +++ b/editor/fileserver/editor_file_server.h @@ -49,7 +49,7 @@ class EditorFileServer : public Object { struct ClientData { Thread *thread = nullptr; Ref<StreamPeerTCP> connection; - Map<int, FileAccess *> files; + Map<int, Ref<FileAccess>> files; EditorFileServer *efs = nullptr; bool quit = false; }; diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index bbbdd85a5a..778c5c33ff 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -225,7 +225,7 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo Vector<String> favorite_paths = EditorSettings::get_singleton()->get_favorites(); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); bool fav_changed = false; for (int i = favorite_paths.size() - 1; i >= 0; i--) { if (da->dir_exists(favorite_paths[i]) || da->file_exists(favorite_paths[i])) { @@ -539,7 +539,7 @@ void FileSystemDock::_navigate_to_path(const String &p_path, bool p_select_in_fa if (target_path.ends_with("/")) { target_path = target_path.substr(0, target_path.length() - 1); } - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (da->file_exists(p_path)) { path = target_path; } else if (da->dir_exists(p_path)) { @@ -989,7 +989,7 @@ void FileSystemDock::_select_file(const String &p_path, bool p_select_in_favorit { List<String> importer_exts; - ResourceImporterScene::get_singleton()->get_recognized_extensions(&importer_exts); + ResourceImporterScene::get_scene_singleton()->get_recognized_extensions(&importer_exts); String extension = fpath.get_extension(); for (const String &E : importer_exts) { if (extension.nocasecmp_to(E) == 0) { @@ -1000,7 +1000,27 @@ void FileSystemDock::_select_file(const String &p_path, bool p_select_in_favorit } if (is_imported) { - ResourceImporterScene::get_singleton()->show_advanced_options(fpath); + ResourceImporterScene::get_scene_singleton()->show_advanced_options(fpath); + } else { + EditorNode::get_singleton()->open_request(fpath); + } + } else if (ResourceLoader::get_resource_type(fpath) == "AnimationLibrary") { + bool is_imported = false; + + { + List<String> importer_exts; + ResourceImporterScene::get_animation_singleton()->get_recognized_extensions(&importer_exts); + String extension = fpath.get_extension(); + for (const String &E : importer_exts) { + if (extension.nocasecmp_to(E) == 0) { + is_imported = true; + break; + } + } + } + + if (is_imported) { + ResourceImporterScene::get_animation_singleton()->show_advanced_options(fpath); } else { EditorNode::get_singleton()->open_request(fpath); } @@ -1183,7 +1203,7 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_ _get_all_items_in_dir(EditorFileSystem::get_singleton()->get_filesystem_path(old_path), file_changed_paths, folder_changed_paths); } - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); print_verbose("Moving " + old_path + " -> " + new_path); Error err = da->rename(old_path, new_path); if (err == OK) { @@ -1241,7 +1261,7 @@ void FileSystemDock::_try_duplicate_item(const FileOrFolder &p_item, const Strin return; } - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); print_verbose("Duplicating " + old_path + " -> " + new_path); Error err = p_item.is_file ? da->copy(old_path, new_path) : da->copy_dir(old_path, new_path); if (err == OK) { @@ -1260,7 +1280,7 @@ void FileSystemDock::_try_duplicate_item(const FileOrFolder &p_item, const Strin cfg->save(new_path + ".import"); } else if (p_item.is_file && (old_path.get_extension() == "tscn" || old_path.get_extension() == "tres")) { // FIXME: Quick hack to fix text resources. This should be fixed properly. - FileAccessRef file = FileAccess::open(old_path, FileAccess::READ, &err); + Ref<FileAccess> file = FileAccess::open(old_path, FileAccess::READ, &err); if (err == OK) { PackedStringArray lines = file->get_as_utf8_string().split("\n"); String line = lines[0]; @@ -1269,7 +1289,7 @@ void FileSystemDock::_try_duplicate_item(const FileOrFolder &p_item, const Strin line = line.substr(0, line.find(" uid")) + "]"; lines.write[0] = line; - FileAccessRef file2 = FileAccess::open(new_path, FileAccess::WRITE, &err); + Ref<FileAccess> file2 = FileAccess::open(new_path, FileAccess::WRITE, &err); if (err == OK) { file2->store_string(String("\n").join(lines)); } @@ -1429,7 +1449,7 @@ void FileSystemDock::_make_dir_confirm() { } print_verbose("Making folder " + dir_name + " in " + directory); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); Error err = da->change_dir(directory); ERR_FAIL_COND_MSG(err != OK, "Cannot open directory '" + directory + "'."); @@ -1479,7 +1499,7 @@ void FileSystemDock::_make_scene_confirm() { scene_name = directory.plus_file(scene_name); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (da->file_exists(scene_name)) { EditorNode::get_singleton()->show_warning(TTR("A file or folder with this name already exists.")); return; @@ -1494,7 +1514,7 @@ void FileSystemDock::_file_removed(String p_file) { // Find the closest parent directory available, in case multiple items were deleted along the same path. path = p_file.get_base_dir(); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); while (!da->dir_exists(path)) { path = path.get_base_dir(); } @@ -1507,7 +1527,7 @@ void FileSystemDock::_folder_removed(String p_folder) { // Find the closest parent directory available, in case multiple items were deleted along the same path. path = p_folder.get_base_dir(); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); while (!da->dir_exists(path)) { path = path.get_base_dir(); } @@ -1546,7 +1566,7 @@ void FileSystemDock::_rename_operation_confirm() { } // Present a more user friendly warning for name conflict. - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); #if defined(WINDOWS_ENABLED) || defined(UWP_ENABLED) // Workaround case insensitivity on Windows. if ((da->file_exists(new_path) || da->dir_exists(new_path)) && new_path.to_lower() != old_path.to_lower()) { @@ -1599,7 +1619,7 @@ void FileSystemDock::_duplicate_operation_confirm() { String new_path = base_dir.plus_file(new_name); // Present a more user friendly warning for name conflict - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (da->file_exists(new_path) || da->dir_exists(new_path)) { EditorNode::get_singleton()->show_warning(TTR("A file or folder with this name already exists.")); return; @@ -2354,7 +2374,7 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, } int exist_counter = 1; - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); while (da->file_exists(new_path) || da->dir_exists(new_path)) { exist_counter++; new_path = vformat(new_path_base, exist_counter); @@ -2831,7 +2851,7 @@ void FileSystemDock::_get_imported_files(const String &p_path, Vector<String> &f return; } - DirAccessRef da = DirAccess::open(p_path); + Ref<DirAccess> da = DirAccess::open(p_path); da->list_dir_begin(); String n = da->get_next(); while (!n.is_empty()) { diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 0dfaaaa1f4..41191271a1 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -211,8 +211,8 @@ float FindInFiles::get_progress() const { } void FindInFiles::_scan_dir(String path, PackedStringArray &out_folders) { - DirAccessRef dir = DirAccess::open(path); - if (!dir) { + Ref<DirAccess> dir = DirAccess::open(path); + if (dir.is_null()) { print_verbose("Cannot open directory! " + path); return; } @@ -253,8 +253,8 @@ void FindInFiles::_scan_dir(String path, PackedStringArray &out_folders) { } void FindInFiles::_scan_file(String fpath) { - FileAccessRef f = FileAccess::open(fpath, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(fpath, FileAccess::READ); + if (f.is_null()) { print_verbose(String("Cannot open file ") + fpath); return; } @@ -274,8 +274,6 @@ void FindInFiles::_scan_file(String fpath) { emit_signal(SNAME(SIGNAL_RESULT_FOUND), fpath, line_number, begin, end, line); } } - - f->close(); } void FindInFiles::_bind_methods() { @@ -873,7 +871,7 @@ void FindInFilesPanel::_on_replace_all_clicked() { // Same as get_line, but preserves line ending characters. class ConservativeGetLine { public: - String get_line(FileAccess *f) { + String get_line(Ref<FileAccess> f) { _line_buffer.clear(); char32_t c = f->get_8(); @@ -908,8 +906,8 @@ void FindInFilesPanel::apply_replaces_in_file(String fpath, const Vector<Result> // If there are unsaved changes, the user will be asked on focus, // however that means either losing changes or losing replaces. - FileAccessRef f = FileAccess::open(fpath, FileAccess::READ); - ERR_FAIL_COND_MSG(!f, "Cannot open file from path '" + fpath + "'."); + Ref<FileAccess> f = FileAccess::open(fpath, FileAccess::READ); + ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file from path '" + fpath + "'."); String buffer; int current_line = 1; @@ -958,8 +956,6 @@ void FindInFilesPanel::apply_replaces_in_file(String fpath, const Vector<Result> ERR_FAIL_COND_MSG(err != OK, "Cannot create file in path '" + fpath + "'."); f->store_string(buffer); - - f->close(); } String FindInFilesPanel::get_replace_text() { diff --git a/editor/icons/AnimationLibrary.svg b/editor/icons/AnimationLibrary.svg new file mode 100644 index 0000000000..0bac67d302 --- /dev/null +++ b/editor/icons/AnimationLibrary.svg @@ -0,0 +1 @@ +<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14.519 2.006A6 6 0 0 0 8.599 8a6 6 0 0 0 5.92 5.994v-1.01a1 1 0 0 1-.92-.984 1 1 0 0 1 .92-.984V4.984a1 1 0 0 1-.92-.984 1 1 0 0 1 .92-.984Zm-3.432 2.996a1 1 0 0 1 .547.133 1 1 0 0 1 .367 1.365 1 1 0 0 1-1.367.365A1 1 0 0 1 10.27 5.5a1 1 0 0 1 .818-.498ZM11.111 9a1 1 0 0 1 .89.5 1 1 0 0 1-.367 1.365 1 1 0 0 1-1.365-.365 1 1 0 0 1 .365-1.365A1 1 0 0 1 11.111 9Z" style="fill:#e0e0e0;fill-opacity:1"/><path d="M11.094 2.104a6 6 0 0 0-5.92 5.994 6 6 0 0 0 5.92 5.994v-.023a5.795 6.506 0 0 1-2.89-3.104 1 1 0 0 1-1.36-.367 1 1 0 0 1 .365-1.365 1 1 0 0 1 .475-.135 5.795 6.506 0 0 1-.076-.984 5.795 6.506 0 0 1 .082-1.027 1 1 0 0 1-.48-.124 1 1 0 0 1-.366-1.365 1 1 0 0 1 .818-.498 1 1 0 0 1 .547.133 1 1 0 0 1 .004.002 5.795 6.506 0 0 1 2.881-3.076z" style="fill:#e0e0e0;fill-opacity:1"/><path d="M7.616 2.104a6 6 0 0 0-5.92 5.994 6 6 0 0 0 5.92 5.994v-.023a5.795 6.506 0 0 1-2.89-3.104 1 1 0 0 1-1.36-.367 1 1 0 0 1 .366-1.365 1 1 0 0 1 .474-.135 5.795 6.506 0 0 1-.076-.984 5.795 6.506 0 0 1 .082-1.027 1 1 0 0 1-.48-.124 1 1 0 0 1-.366-1.365 1 1 0 0 1 .819-.498 1 1 0 0 1 .547.133 1 1 0 0 1 .003.002 5.795 6.506 0 0 1 2.881-3.076z" style="fill:#e0e0e0;fill-opacity:1"/></svg> diff --git a/editor/icons/ArrayOccluder3D.svg b/editor/icons/ArrayOccluder3D.svg new file mode 100644 index 0000000000..ac45821897 --- /dev/null +++ b/editor/icons/ArrayOccluder3D.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m13 1c-1.104569 0-2 .8954305-2 2s.895431 2 2 2 2-.8954305 2-2-.895431-2-2-2zm-2 7v3h-3v2h3v3h2v-3h3v-2h-3v-3zm-8 3c-1.1045695 0-2 .895431-2 2s.8954305 2 2 2 2-.895431 2-2-.8954305-2-2-2z" fill="#ffca5f"/></svg> diff --git a/editor/icons/BoxOccluder3D.svg b/editor/icons/BoxOccluder3D.svg new file mode 100644 index 0000000000..3cee3db532 --- /dev/null +++ b/editor/icons/BoxOccluder3D.svg @@ -0,0 +1 @@ +<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 .88867188-.5058594.25390622a4.5 4.5 0 0 1 1.3789063 2.2988281l3.0664061 1.5332032-3.5546874 1.7753906a4.5 4.5 0 0 1 -1.6796875 1.6601562l.2949219.1464844v3.9414064l-4-2.001953v-1.7636721a4.5 4.5 0 0 1 -2-1.4179688v4.2968749l7 3.5 7-3.5v-7.2226561zm5 5.66796872v3.9394534l-4 2.001953v-3.9414064z"/><path d="m8 .88867188-.5058594.25390622a4.5 4.5 0 0 1 1.5058594 3.3574219 4.5 4.5 0 0 1 -4.5 4.5 4.5 4.5 0 0 1 -3.5-1.6855469v4.2968749l7 3.5 7-3.5v-7.2226561z" fill="#ffca5f"/></svg> diff --git a/editor/icons/GuiVsplitBg.svg b/editor/icons/GuiVsplitBg.svg deleted file mode 100644 index 9844fc2018..0000000000 --- a/editor/icons/GuiVsplitBg.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="8" viewBox="0 0 8 7.9999995" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h8v8h-8z" fill-opacity=".098039"/></svg> diff --git a/editor/icons/Occluder3D.svg b/editor/icons/Occluder3D.svg index 850e2651af..c91a77781b 100644 --- a/editor/icons/Occluder3D.svg +++ b/editor/icons/Occluder3D.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7.90625 1a7 7 0 0 0 -1.2988281.1386719 4.5 4.5 0 0 1 3.3925781 4.3613281 4.5 4.5 0 0 1 -4.5 4.5 4.5 4.5 0 0 1 -4.359375-3.3886719 7 7 0 0 0 -.140625 1.3886719 7 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 -.09375 0z" fill="#ffca5f" stroke-width=".365215"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m13 1a2 2 0 0 0 -1.730469 1h-3.0273435a4.5 4.5 0 0 1 .7285156 2h2.3007809a2 2 0 0 0 .728516.7304688v5.8554692l-3.6933594-3.6933599a4.5 4.5 0 0 1 -1.4140625 1.4140625l3.6933599 3.6933594h-5.8574224a2 2 0 0 0 -.7285156-.730469v-2.3046872a4.5 4.5 0 0 1 -2-.7285157v3.0351559a2 2 0 0 0 -1 1.728516 2 2 0 0 0 2 2 2 2 0 0 0 1.7304688-1h6.5410152a2 2 0 0 0 1.728516 1 2 2 0 0 0 2-2 2 2 0 0 0 -1.03125-1.75h.03125v-6.5214844a2 2 0 0 0 1-1.7285156 2 2 0 0 0 -2-2z" fill="#ffca5f"/></svg> diff --git a/editor/icons/PolygonOccluder3D.svg b/editor/icons/PolygonOccluder3D.svg new file mode 100644 index 0000000000..fc87e5e086 --- /dev/null +++ b/editor/icons/PolygonOccluder3D.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#ffca5f" stroke-linejoin="round"><path d="m8.2421875 2a4.5 4.5 0 0 1 .7578125 2.5 4.5 4.5 0 0 1 -4.5 4.5 4.5 4.5 0 0 1 -2.5-.7636719v5.7636719h12l-6-6 6-6z" stroke-width="2"/><path d="m7.328125 1c.6472144.5230929 1.136703 1.2154082 1.4140625 2h2.8437505l-2.7675786 2.767578c-.2943505.9927946-.9220914 1.8536963-1.7773438 2.4375.0343146.1879491.1217471.3621363.2519532.501953l4.2929692 4.292969h-8.585938v-4.267578c-.785054-.2784421-1.4774185-.7693178-2-1.417969v6.685547c.0000552.552262.4477381.999945 1 1h12c.890637-.00035 1.336587-1.077036.707031-1.707031l-5.2929685-5.292969 5.2929685-5.292969c.629556-.6299945.183606-1.7066812-.707031-1.707031z"/></g></svg> diff --git a/editor/icons/QuadOccluder3D.svg b/editor/icons/QuadOccluder3D.svg new file mode 100644 index 0000000000..16da6f420f --- /dev/null +++ b/editor/icons/QuadOccluder3D.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7.328125 1c.6472144.5230929 1.136703 1.2154082 1.4140625 2h4.2578125v8.585938l-4.6933594-4.6933599c-.3593282.5714479-.8426146 1.0547343-1.4140625 1.4140625l4.6933599 4.6933594h-8.585938v-4.2675781c-.785054-.2784421-1.4774185-.7693176-2-1.4179688v7.6855469h14v-14z" fill="#ffca5f"/></svg> diff --git a/editor/icons/SphereOccluder3D.svg b/editor/icons/SphereOccluder3D.svg new file mode 100644 index 0000000000..850e2651af --- /dev/null +++ b/editor/icons/SphereOccluder3D.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7.90625 1a7 7 0 0 0 -1.2988281.1386719 4.5 4.5 0 0 1 3.3925781 4.3613281 4.5 4.5 0 0 1 -4.5 4.5 4.5 4.5 0 0 1 -4.359375-3.3886719 7 7 0 0 0 -.140625 1.3886719 7 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 -.09375 0z" fill="#ffca5f" stroke-width=".365215"/></svg> diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index 69fa64c24c..013dcb5deb 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -1801,7 +1801,14 @@ Node *EditorSceneFormatImporterCollada::import_scene(const String &p_path, uint3 name = state.animations[i]->get_name(); } - ap->add_animation(name, state.animations[i]); + Ref<AnimationLibrary> library; + if (!ap->has_animation_library("")) { + library.instantiate(); + ap->add_animation_library("", library); + } else { + library = ap->get_animation_library(""); + } + library->add_animation(name, state.animations[i]); } state.scene->add_child(ap, true); ap->set_owner(state.scene); @@ -1810,26 +1817,5 @@ Node *EditorSceneFormatImporterCollada::import_scene(const String &p_path, uint3 return state.scene; } -Ref<Animation> EditorSceneFormatImporterCollada::import_animation(const String &p_path, uint32_t p_flags, const Map<StringName, Variant> &p_options, int p_bake_fps) { - ColladaImport state; - - state.use_mesh_builtin_materials = false; - - Error err = state.load(p_path, Collada::IMPORT_FLAG_ANIMATION, p_flags & EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS); - ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'."); - - state.create_animations(true); - if (state.scene) { - memdelete(state.scene); - } - - if (state.animations.size() == 0) { - return Ref<Animation>(); - } - Ref<Animation> anim = state.animations[0]; - - return anim; -} - EditorSceneFormatImporterCollada::EditorSceneFormatImporterCollada() { } diff --git a/editor/import/editor_import_collada.h b/editor/import/editor_import_collada.h index c32d785d1c..be3f74d821 100644 --- a/editor/import/editor_import_collada.h +++ b/editor/import/editor_import_collada.h @@ -40,7 +40,6 @@ public: virtual uint32_t get_import_flags() const override; virtual void get_extensions(List<String> *r_extensions) const override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const Map<StringName, Variant> &p_options, int p_bake_fps, List<String> *r_missing_deps = nullptr, Error *r_err = nullptr) override; - virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, const Map<StringName, Variant> &p_options, int p_bake_fps) override; EditorSceneFormatImporterCollada(); }; diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp index f0ee14bdcb..ee6500a643 100644 --- a/editor/import/resource_importer_csv_translation.cpp +++ b/editor/import/resource_importer_csv_translation.cpp @@ -88,9 +88,8 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const break; } - FileAccessRef f = FileAccess::open(p_source_file, FileAccess::READ); - - ERR_FAIL_COND_V_MSG(!f, ERR_INVALID_PARAMETER, "Cannot open file from path '" + p_source_file + "'."); + Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_INVALID_PARAMETER, "Cannot open file from path '" + p_source_file + "'."); Vector<String> line = f->get_csv_line(delimiter); ERR_FAIL_COND_V(line.size() <= 1, ERR_PARSE_ERROR); diff --git a/editor/import/resource_importer_image.cpp b/editor/import/resource_importer_image.cpp index e6a822d827..8514df76bb 100644 --- a/editor/import/resource_importer_image.cpp +++ b/editor/import/resource_importer_image.cpp @@ -71,10 +71,9 @@ void ResourceImporterImage::get_import_options(const String &p_path, List<Import } Error ResourceImporterImage::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ); - - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file from path '" + p_source_file + "'."); + Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file from path '" + p_source_file + "'."); uint64_t len = f->get_length(); Vector<uint8_t> data; @@ -82,10 +81,8 @@ Error ResourceImporterImage::import(const String &p_source_file, const String &p f->get_buffer(data.ptrw(), len); - memdelete(f); - f = FileAccess::open(p_save_path + ".image", FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_CREATE, "Cannot create file in path '" + p_save_path + ".image'."); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_CREATE, "Cannot create file in path '" + p_save_path + ".image'."); //save the header GDIM const uint8_t header[4] = { 'G', 'D', 'I', 'M' }; @@ -95,8 +92,6 @@ Error ResourceImporterImage::import(const String &p_source_file, const String &p //SAVE the actual image f->store_buffer(data.ptr(), len); - memdelete(f); - return OK; } diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 9ddee9c058..7c0c99cd29 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -257,7 +257,7 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons } } - FileAccessRef f = FileAccess::open(p_to_path, FileAccess::WRITE); + Ref<FileAccess> f = FileAccess::open(p_to_path, FileAccess::WRITE); f->store_8('G'); f->store_8('S'); f->store_8('T'); @@ -280,8 +280,6 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons for (int i = 0; i < mipmap_images.size(); i++) { ResourceImporterTexture::save_to_ctex_format(f, mipmap_images[i], ResourceImporterTexture::CompressMode(p_compress_mode), used_channels, p_vram_compression, p_lossy); } - - f->close(); } Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp index 9042f1e32c..88837d089a 100644 --- a/editor/import/resource_importer_obj.cpp +++ b/editor/import/resource_importer_obj.cpp @@ -44,8 +44,8 @@ uint32_t EditorOBJImporter::get_import_flags() const { } static Error _parse_material_library(const String &p_path, Map<String, Ref<StandardMaterial3D>> &material_map, List<String> *r_missing_deps) { - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Couldn't open MTL file '%s', it may not exist or not be readable.", p_path)); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open MTL file '%s', it may not exist or not be readable.", p_path)); Ref<StandardMaterial3D> current; String current_name; @@ -203,8 +203,8 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Stand } static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_optimize, Vector3 p_scale_mesh, Vector3 p_offset_mesh, List<String> *r_missing_deps) { - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path)); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path)); Ref<ArrayMesh> mesh; mesh.instantiate(); @@ -457,10 +457,6 @@ Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, co return scene; } -Ref<Animation> EditorOBJImporter::import_animation(const String &p_path, uint32_t p_flags, const Map<StringName, Variant> &p_options, int p_bake_fps) { - return Ref<Animation>(); -} - void EditorOBJImporter::get_extensions(List<String> *r_extensions) const { r_extensions->push_back("obj"); } diff --git a/editor/import/resource_importer_obj.h b/editor/import/resource_importer_obj.h index d7e3f0209d..1b5e8bbdc1 100644 --- a/editor/import/resource_importer_obj.h +++ b/editor/import/resource_importer_obj.h @@ -40,7 +40,6 @@ public: virtual uint32_t get_import_flags() const override; virtual void get_extensions(List<String> *r_extensions) const override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const Map<StringName, Variant> &p_options, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr) override; - virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, const Map<StringName, Variant> &p_options, int p_bake_fps) override; EditorOBJImporter(); }; diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index e7c605aaf0..bdb0c3c493 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -87,26 +87,13 @@ Node *EditorSceneFormatImporter::import_scene(const String &p_path, uint32_t p_f ERR_FAIL_V(nullptr); } -Ref<Animation> EditorSceneFormatImporter::import_animation(const String &p_path, uint32_t p_flags, const Map<StringName, Variant> &p_options, int p_bake_fps) { - Dictionary options_dict; - for (const KeyValue<StringName, Variant> &elem : p_options) { - options_dict[elem.key] = elem.value; - } - Ref<Animation> ret; - if (GDVIRTUAL_CALL(_import_animation, p_path, p_flags, options_dict, p_bake_fps, ret)) { - return ret; - } - - ERR_FAIL_V(nullptr); -} - void EditorSceneFormatImporter::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) { GDVIRTUAL_CALL(_get_import_options, p_path); } -Variant EditorSceneFormatImporter::get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) { +Variant EditorSceneFormatImporter::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const Map<StringName, Variant> &p_options) { Variant ret; - GDVIRTUAL_CALL(_get_option_visibility, p_path, p_option, ret); + GDVIRTUAL_CALL(_get_option_visibility, p_path, p_for_animation, p_option, ret); return ret; } @@ -114,15 +101,15 @@ void EditorSceneFormatImporter::_bind_methods() { GDVIRTUAL_BIND(_get_import_flags); GDVIRTUAL_BIND(_get_extensions); GDVIRTUAL_BIND(_import_scene, "path", "flags", "options", "bake_fps"); - GDVIRTUAL_BIND(_import_animation, "path", "flags", "options", "bake_fps"); GDVIRTUAL_BIND(_get_import_options, "path"); - GDVIRTUAL_BIND(_get_option_visibility, "path", "option"); + GDVIRTUAL_BIND(_get_option_visibility, "path", "for_animation", "option"); BIND_CONSTANT(IMPORT_SCENE); BIND_CONSTANT(IMPORT_ANIMATION); BIND_CONSTANT(IMPORT_FAIL_ON_MISSING_DEPENDENCIES); BIND_CONSTANT(IMPORT_GENERATE_TANGENT_ARRAYS); BIND_CONSTANT(IMPORT_USE_NAMED_SKIN_BINDS); + BIND_CONSTANT(IMPORT_DISCARD_MESHES_AND_MATERIALS); } ///////////////////////////////// @@ -179,10 +166,10 @@ void EditorScenePostImportPlugin::get_internal_import_options(InternalImportCate GDVIRTUAL_CALL(_get_internal_import_options, p_category); current_option_list = nullptr; } -Variant EditorScenePostImportPlugin::get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const { +Variant EditorScenePostImportPlugin::get_internal_option_visibility(InternalImportCategory p_category, bool p_for_animation, const String &p_option, const Map<StringName, Variant> &p_options) const { current_options = &p_options; Variant ret; - GDVIRTUAL_CALL(_get_internal_option_visibility, p_category, p_option, ret); + GDVIRTUAL_CALL(_get_internal_option_visibility, p_category, p_for_animation, p_option, ret); current_options = nullptr; return ret; } @@ -205,10 +192,10 @@ void EditorScenePostImportPlugin::get_import_options(const String &p_path, List< GDVIRTUAL_CALL(_get_import_options, p_path); current_option_list = nullptr; } -Variant EditorScenePostImportPlugin::get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const { +Variant EditorScenePostImportPlugin::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const Map<StringName, Variant> &p_options) const { current_options = &p_options; Variant ret; - GDVIRTUAL_CALL(_get_option_visibility, p_path, p_option, ret); + GDVIRTUAL_CALL(_get_option_visibility, p_path, p_for_animation, p_option, ret); current_options = nullptr; return ret; } @@ -231,11 +218,11 @@ void EditorScenePostImportPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("add_import_option_advanced", "type", "name", "default_value", "hint", "hint_string", "usage_flags"), &EditorScenePostImportPlugin::add_import_option_advanced, DEFVAL(PROPERTY_HINT_NONE), DEFVAL(""), DEFVAL(PROPERTY_USAGE_DEFAULT)); GDVIRTUAL_BIND(_get_internal_import_options, "category"); - GDVIRTUAL_BIND(_get_internal_option_visibility, "category", "option"); + GDVIRTUAL_BIND(_get_internal_option_visibility, "category", "for_animation", "option"); GDVIRTUAL_BIND(_get_internal_option_update_view_required, "category", "option"); GDVIRTUAL_BIND(_internal_process, "category", "base_node", "node", "resource"); GDVIRTUAL_BIND(_get_import_options, "path"); - GDVIRTUAL_BIND(_get_option_visibility, "path", "option"); + GDVIRTUAL_BIND(_get_option_visibility, "path", "for_animation", "option"); GDVIRTUAL_BIND(_pre_process, "scene"); GDVIRTUAL_BIND(_post_process, "scene"); @@ -251,11 +238,11 @@ void EditorScenePostImportPlugin::_bind_methods() { ///////////////////////////////////////////////////////// String ResourceImporterScene::get_importer_name() const { - return "scene"; + return animation_importer ? "animation_library" : "scene"; } String ResourceImporterScene::get_visible_name() const { - return "Scene"; + return animation_importer ? "Animation Library" : "Scene"; } void ResourceImporterScene::get_recognized_extensions(List<String> *p_extensions) const { @@ -265,11 +252,11 @@ void ResourceImporterScene::get_recognized_extensions(List<String> *p_extensions } String ResourceImporterScene::get_save_extension() const { - return "scn"; + return animation_importer ? "res" : "scn"; } String ResourceImporterScene::get_resource_type() const { - return "PackedScene"; + return animation_importer ? "AnimationLibrary" : "PackedScene"; } int ResourceImporterScene::get_format_version() const { @@ -277,26 +264,34 @@ int ResourceImporterScene::get_format_version() const { } bool ResourceImporterScene::get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const { - if (p_option.begins_with("animation/")) { + if (animation_importer) { + if (p_option == "animation/import") { // Option ignored, animation always imported. + return false; + } + } else if (p_option.begins_with("animation/")) { if (p_option != "animation/import" && !bool(p_options["animation/import"])) { return false; } } + if (animation_importer && (p_option.begins_with("nodes/") || p_option.begins_with("meshes/") || p_option.begins_with("skins/"))) { + return false; // Nothing to do here for animations. + } + if (p_option == "meshes/lightmap_texel_size" && int(p_options["meshes/light_baking"]) != 2) { // Only display the lightmap texel size import option when using the Static Lightmaps light baking mode. return false; } for (int i = 0; i < post_importer_plugins.size(); i++) { - Variant ret = post_importer_plugins.write[i]->get_option_visibility(p_path, p_option, p_options); + Variant ret = post_importer_plugins.write[i]->get_option_visibility(p_path, animation_importer, p_option, p_options); if (ret.get_type() == Variant::BOOL) { return ret; } } for (Ref<EditorSceneFormatImporter> importer : importers) { - Variant ret = importer->get_option_visibility(p_path, p_option, p_options); + Variant ret = importer->get_option_visibility(p_path, animation_importer, p_option, p_options); if (ret.get_type() == Variant::BOOL) { return ret; } @@ -473,7 +468,9 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I if (_teststr(animname, loop_strings[i])) { anim->set_loop_mode(Animation::LoopMode::LOOP_LINEAR); animname = _fixstr(animname, loop_strings[i]); - ap->rename_animation(E, animname); + + Ref<AnimationLibrary> library = ap->get_animation_library(ap->find_animation_library(anim)); + library->rename_animation(E, animname); } } } @@ -1019,7 +1016,8 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref< Ref<Animation> saved_anim = _save_animation_to_file(anim, save, path, keep_custom); if (saved_anim != anim) { - ap->add_animation(name, saved_anim); //replace + Ref<AnimationLibrary> al = ap->get_animation_library(ap->find_animation_library(anim)); + al->add_animation(name, saved_anim); //replace } } } @@ -1109,6 +1107,7 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_ } Ref<Animation> default_anim = anim->get_animation("default"); + Ref<AnimationLibrary> al = anim->get_animation_library(anim->find_animation(default_anim)); for (int i = 0; i < p_clips.size(); i += 7) { String name = p_clips[i]; @@ -1246,15 +1245,16 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_ new_anim->set_loop_mode(loop_mode); new_anim->set_length(to - from); - anim->add_animation(name, new_anim); + + al->add_animation(name, new_anim); Ref<Animation> saved_anim = _save_animation_to_file(new_anim, save_to_file, save_to_path, keep_current); if (saved_anim != new_anim) { - anim->add_animation(name, saved_anim); + al->add_animation(name, saved_anim); } } - anim->remove_animation("default"); //remove default (no longer needed) + al->remove_animation("default"); // Remove default (no longer needed). } void ResourceImporterScene::_optimize_animations(AnimationPlayer *anim, float p_max_lin_error, float p_max_ang_error, float p_max_angle) { @@ -1477,7 +1477,7 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor } for (int i = 0; i < post_importer_plugins.size(); i++) { - Variant ret = post_importer_plugins.write[i]->get_internal_option_visibility(EditorScenePostImportPlugin::InternalImportCategory(p_category), p_option, p_options); + Variant ret = post_importer_plugins.write[i]->get_internal_option_visibility(EditorScenePostImportPlugin::InternalImportCategory(p_category), animation_importer, p_option, p_options); if (ret.get_type() == Variant::BOOL) { return ret; } @@ -1969,8 +1969,13 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p int import_flags = 0; - if (bool(p_options["animation/import"])) { + if (animation_importer) { import_flags |= EditorSceneFormatImporter::IMPORT_ANIMATION; + import_flags |= EditorSceneFormatImporter::IMPORT_DISCARD_MESHES_AND_MATERIALS; + } else { + if (bool(p_options["animation/import"])) { + import_flags |= EditorSceneFormatImporter::IMPORT_ANIMATION; + } } if (bool(p_options["skins/use_named_skins"])) { @@ -2086,14 +2091,13 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p _generate_meshes(scene, mesh_data, gen_lods, create_shadow_meshes, LightBakeMode(light_bake_mode), lightmap_texel_size, src_lightmap_cache, mesh_lightmap_caches); if (mesh_lightmap_caches.size()) { - FileAccessRef f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE); - if (f) { + Ref<FileAccess> f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE); + if (f.is_valid()) { f->store_32(mesh_lightmap_caches.size()); for (int i = 0; i < mesh_lightmap_caches.size(); i++) { String md5 = String::md5(mesh_lightmap_caches[i].ptr()); f->store_buffer(mesh_lightmap_caches[i].ptr(), mesh_lightmap_caches[i].size()); } - f->close(); } } err = OK; @@ -2135,11 +2139,35 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p progress.step(TTR("Saving..."), 104); - Ref<PackedScene> packer = memnew(PackedScene); - packer->pack(scene); - print_verbose("Saving scene to: " + p_save_path + ".scn"); - err = ResourceSaver::save(p_save_path + ".scn", packer); //do not take over, let the changed files reload themselves - ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save scene to file '" + p_save_path + ".scn'."); + if (animation_importer) { + Ref<AnimationLibrary> library; + for (int i = 0; i < scene->get_child_count(); i++) { + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(scene->get_child(i)); + if (ap) { + List<StringName> libs; + ap->get_animation_library_list(&libs); + if (libs.size()) { + library = ap->get_animation_library(libs.front()->get()); + break; + } + } + } + + if (!library.is_valid()) { + library.instantiate(); // Will be empty + } + + print_verbose("Saving animation to: " + p_save_path + ".scn"); + err = ResourceSaver::save(p_save_path + ".res", library); //do not take over, let the changed files reload themselves + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save animation to file '" + p_save_path + ".res'."); + + } else { + Ref<PackedScene> packer = memnew(PackedScene); + packer->pack(scene); + print_verbose("Saving scene to: " + p_save_path + ".scn"); + err = ResourceSaver::save(p_save_path + ".scn", packer); //do not take over, let the changed files reload themselves + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save scene to file '" + p_save_path + ".scn'."); + } memdelete(scene); @@ -2149,17 +2177,26 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p return OK; } -ResourceImporterScene *ResourceImporterScene::singleton = nullptr; +ResourceImporterScene *ResourceImporterScene::scene_singleton = nullptr; +ResourceImporterScene *ResourceImporterScene::animation_singleton = nullptr; + +Vector<Ref<EditorSceneFormatImporter>> ResourceImporterScene::importers; +Vector<Ref<EditorScenePostImportPlugin>> ResourceImporterScene::post_importer_plugins; bool ResourceImporterScene::ResourceImporterScene::has_advanced_options() const { return true; } void ResourceImporterScene::ResourceImporterScene::show_advanced_options(const String &p_path) { - SceneImportSettings::get_singleton()->open_settings(p_path); + SceneImportSettings::get_singleton()->open_settings(p_path, animation_importer); } -ResourceImporterScene::ResourceImporterScene() { - singleton = this; +ResourceImporterScene::ResourceImporterScene(bool p_animation_import) { + if (p_animation_import) { + animation_singleton = this; + } else { + scene_singleton = this; + } + animation_importer = p_animation_import; } void ResourceImporterScene::add_importer(Ref<EditorSceneFormatImporter> p_importer, bool p_first_priority) { @@ -2188,6 +2225,11 @@ void ResourceImporterScene::remove_importer(Ref<EditorSceneFormatImporter> p_imp importers.erase(p_importer); } +void ResourceImporterScene::clean_up_importer_plugins() { + importers.clear(); + post_importer_plugins.clear(); +} + /////////////////////////////////////// uint32_t EditorSceneFormatImporterESCN::get_import_flags() const { @@ -2208,7 +2250,3 @@ Node *EditorSceneFormatImporterESCN::import_scene(const String &p_path, uint32_t return scene; } - -Ref<Animation> EditorSceneFormatImporterESCN::import_animation(const String &p_path, uint32_t p_flags, const Map<StringName, Variant> &p_options, int p_bake_fps) { - ERR_FAIL_V(Ref<Animation>()); -} diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index a819be682d..368f68ae8f 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -56,9 +56,8 @@ protected: GDVIRTUAL0RC(int, _get_import_flags) GDVIRTUAL0RC(Vector<String>, _get_extensions) GDVIRTUAL4R(Object *, _import_scene, String, uint32_t, Dictionary, uint32_t) - GDVIRTUAL4R(Ref<Animation>, _import_animation, String, uint32_t, Dictionary, uint32_t) GDVIRTUAL1(_get_import_options, String) - GDVIRTUAL2RC(Variant, _get_option_visibility, String, String) + GDVIRTUAL3RC(Variant, _get_option_visibility, String, bool, String) public: enum ImportFlags { @@ -67,14 +66,14 @@ public: IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 4, IMPORT_GENERATE_TANGENT_ARRAYS = 8, IMPORT_USE_NAMED_SKIN_BINDS = 16, + IMPORT_DISCARD_MESHES_AND_MATERIALS = 32, //used for optimizing animation import }; virtual uint32_t get_import_flags() const; virtual void get_extensions(List<String> *r_extensions) const; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const Map<StringName, Variant> &p_options, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr); - virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, const Map<StringName, Variant> &p_options, int p_bake_fps); virtual void get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options); - virtual Variant get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options); + virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const Map<StringName, Variant> &p_options); EditorSceneFormatImporter() {} }; @@ -118,11 +117,11 @@ private: protected: GDVIRTUAL1(_get_internal_import_options, int) - GDVIRTUAL2RC(Variant, _get_internal_option_visibility, int, String) + GDVIRTUAL3RC(Variant, _get_internal_option_visibility, int, bool, String) GDVIRTUAL2RC(Variant, _get_internal_option_update_view_required, int, String) GDVIRTUAL4(_internal_process, int, Node *, Node *, RES) GDVIRTUAL1(_get_import_options, String) - GDVIRTUAL2RC(Variant, _get_option_visibility, String, String) + GDVIRTUAL3RC(Variant, _get_option_visibility, String, bool, String) GDVIRTUAL1(_pre_process, Node *) GDVIRTUAL1(_post_process, Node *) @@ -134,13 +133,13 @@ public: void add_import_option_advanced(Variant::Type p_type, const String &p_name, Variant p_default_value, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = String(), int p_usage_flags = PROPERTY_USAGE_DEFAULT); virtual void get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options); - virtual Variant get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const; + virtual Variant get_internal_option_visibility(InternalImportCategory p_category, bool p_for_animation, const String &p_option, const Map<StringName, Variant> &p_options) const; virtual Variant get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const; virtual void internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, RES p_resource, const Dictionary &p_options); virtual void get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options); - virtual Variant get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const; + virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const Map<StringName, Variant> &p_options) const; virtual void pre_process(Node *p_scene, const Map<StringName, Variant> &p_options); virtual void post_process(Node *p_scene, const Map<StringName, Variant> &p_options); @@ -153,9 +152,11 @@ VARIANT_ENUM_CAST(EditorScenePostImportPlugin::InternalImportCategory) class ResourceImporterScene : public ResourceImporter { GDCLASS(ResourceImporterScene, ResourceImporter); - Vector<Ref<EditorSceneFormatImporter>> importers; + static Vector<Ref<EditorSceneFormatImporter>> importers; + static Vector<Ref<EditorScenePostImportPlugin>> post_importer_plugins; - static ResourceImporterScene *singleton; + static ResourceImporterScene *scene_singleton; + static ResourceImporterScene *animation_singleton; enum LightBakeMode { LIGHT_BAKE_DISABLED, @@ -225,18 +226,21 @@ class ResourceImporterScene : public ResourceImporter { void _optimize_track_usage(AnimationPlayer *p_player, AnimationImportTracks *p_track_actions); - mutable Vector<Ref<EditorScenePostImportPlugin>> post_importer_plugins; + bool animation_importer = false; public: - static ResourceImporterScene *get_singleton() { return singleton; } + static ResourceImporterScene *get_scene_singleton() { return scene_singleton; } + static ResourceImporterScene *get_animation_singleton() { return animation_singleton; } - void add_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin, bool p_first_priority = false); - void remove_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin); + static void add_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin, bool p_first_priority = false); + static void remove_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin); const Vector<Ref<EditorSceneFormatImporter>> &get_importers() const { return importers; } - void add_importer(Ref<EditorSceneFormatImporter> p_importer, bool p_first_priority = false); - void remove_importer(Ref<EditorSceneFormatImporter> p_importer); + static void add_importer(Ref<EditorSceneFormatImporter> p_importer, bool p_first_priority = false); + static void remove_importer(Ref<EditorSceneFormatImporter> p_importer); + + static void clean_up_importer_plugins(); virtual String get_importer_name() const override; virtual String get_visible_name() const override; @@ -283,7 +287,7 @@ public: virtual bool can_import_threaded() const override { return false; } - ResourceImporterScene(); + ResourceImporterScene(bool p_animation_import = false); template <class M> static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options); @@ -299,7 +303,6 @@ public: virtual uint32_t get_import_flags() const override; virtual void get_extensions(List<String> *r_extensions) const override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const Map<StringName, Variant> &p_options, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr) override; - virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, const Map<StringName, Variant> &p_options, int p_bake_fps) override; }; #include "scene/resources/box_shape_3d.h" diff --git a/editor/import/resource_importer_shader_file.cpp b/editor/import/resource_importer_shader_file.cpp index cc34259a2d..1d70a47daa 100644 --- a/editor/import/resource_importer_shader_file.cpp +++ b/editor/import/resource_importer_shader_file.cpp @@ -82,7 +82,7 @@ static String _include_function(const String &p_path, void *userpointer) { include = base_path->plus_file(include); } - FileAccessRef file_inc = FileAccess::open(include, FileAccess::READ, &err); + Ref<FileAccess> file_inc = FileAccess::open(include, FileAccess::READ, &err); if (err != OK) { return String(); } @@ -93,7 +93,7 @@ Error ResourceImporterShaderFile::import(const String &p_source_file, const Stri /* STEP 1, Read shader code */ Error err; - FileAccessRef file = FileAccess::open(p_source_file, FileAccess::READ, &err); + Ref<FileAccess> file = FileAccess::open(p_source_file, FileAccess::READ, &err); ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN); ERR_FAIL_COND_V(!file.operator->(), ERR_CANT_OPEN); diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index e2fa624fc6..de51a28c5a 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -229,7 +229,7 @@ void ResourceImporterTexture::get_import_options(const String &p_path, List<Impo } } -void ResourceImporterTexture::save_to_ctex_format(FileAccess *f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality) { +void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality) { switch (p_compress_mode) { case COMPRESS_LOSSLESS: { bool lossless_force_png = ProjectSettings::get_singleton()->get("rendering/textures/lossless_compression/force_png") || @@ -322,8 +322,8 @@ void ResourceImporterTexture::save_to_ctex_format(FileAccess *f, const Ref<Image } void ResourceImporterTexture::_save_ctex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_roughness, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel) { - FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE); - ERR_FAIL_NULL(f); + Ref<FileAccess> f = FileAccess::open(p_to_path, FileAccess::WRITE); + ERR_FAIL_COND(f.is_null()); f->store_8('G'); f->store_8('S'); f->store_8('T'); @@ -399,8 +399,6 @@ void ResourceImporterTexture::_save_ctex(const Ref<Image> &p_image, const String Image::UsedChannels used_channels = image->detect_used_channels(csource); save_to_ctex_format(f, image, p_compress_mode, used_channels, p_vram_compression, p_lossy_quality); - - memdelete(f); } Error ResourceImporterTexture::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h index b3a68260fc..b932c598a2 100644 --- a/editor/import/resource_importer_texture.h +++ b/editor/import/resource_importer_texture.h @@ -77,7 +77,7 @@ protected: void _save_ctex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel); public: - static void save_to_ctex_format(FileAccess *f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality); + static void save_to_ctex_format(Ref<FileAccess> f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality); static ResourceImporterTexture *get_singleton() { return singleton; } virtual String get_importer_name() const override; diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index 68d1d23dd8..154970f7ed 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -97,7 +97,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s /* STEP 1, READ WAVE FILE */ Error err; - FileAccess *file = FileAccess::open(p_source_file, FileAccess::READ, &err); + Ref<FileAccess> file = FileAccess::open(p_source_file, FileAccess::READ, &err); ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_OPEN, "Cannot open file '" + p_source_file + "'."); @@ -107,8 +107,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s file->get_buffer((uint8_t *)&riff, 4); //RIFF if (riff[0] != 'R' || riff[1] != 'I' || riff[2] != 'F' || riff[3] != 'F') { - file->close(); - memdelete(file); ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); } @@ -122,8 +120,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s file->get_buffer((uint8_t *)&wave, 4); //RIFF if (wave[0] != 'W' || wave[1] != 'A' || wave[2] != 'V' || wave[3] != 'E') { - file->close(); - memdelete(file); ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Not a WAV file (no WAVE RIFF header)."); } @@ -166,15 +162,11 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s //Consider revision for engine version 3.0 compression_code = file->get_16(); if (compression_code != 1 && compression_code != 3) { - file->close(); - memdelete(file); ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM instead."); } format_channels = file->get_16(); if (format_channels != 1 && format_channels != 2) { - file->close(); - memdelete(file); ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Format not supported for WAVE file (not stereo or mono)."); } @@ -185,8 +177,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s format_bits = file->get_16(); // bits per sample if (format_bits % 8 || format_bits == 0) { - file->close(); - memdelete(file); ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Invalid amount of bits in the sample (should be one of 8, 16, 24 or 32)."); } @@ -206,8 +196,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s frames = chunksize; if (format_channels == 0) { - file->close(); - memdelete(file); ERR_FAIL_COND_V(format_channels == 0, ERR_INVALID_DATA); } frames /= format_channels; @@ -254,8 +242,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s } if (file->eof_reached()) { - file->close(); - memdelete(file); ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Premature end of file."); } } @@ -295,9 +281,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s file->seek(file_pos + chunksize); } - file->close(); - memdelete(file); - // STEP 2, APPLY CONVERSIONS bool is16 = format_bits != 8; diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 302bc98499..4e53a644ee 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -47,6 +47,8 @@ class SceneImportSettingsData : public Object { Map<StringName, Variant> current; Map<StringName, Variant> defaults; List<ResourceImporter::ImportOption> options; + bool hide_options = false; + String path; ResourceImporterScene::InternalImportCategory category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX; @@ -60,8 +62,26 @@ class SceneImportSettingsData : public Object { current[p_name] = p_value; - if (ResourceImporterScene::get_singleton()->get_internal_option_update_view_required(category, p_name, current)) { - SceneImportSettings::get_singleton()->update_view(); + if (SceneImportSettings::get_singleton()->is_editing_animation()) { + if (category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX) { + if (ResourceImporterScene::get_animation_singleton()->get_option_visibility(path, p_name, current)) { + SceneImportSettings::get_singleton()->update_view(); + } + } else { + if (ResourceImporterScene::get_animation_singleton()->get_internal_option_update_view_required(category, p_name, current)) { + SceneImportSettings::get_singleton()->update_view(); + } + } + } else { + if (category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX) { + if (ResourceImporterScene::get_scene_singleton()->get_option_visibility(path, p_name, current)) { + SceneImportSettings::get_singleton()->update_view(); + } + } else { + if (ResourceImporterScene::get_scene_singleton()->get_internal_option_update_view_required(category, p_name, current)) { + SceneImportSettings::get_singleton()->update_view(); + } + } } return true; @@ -82,9 +102,30 @@ class SceneImportSettingsData : public Object { return false; } void _get_property_list(List<PropertyInfo> *p_list) const { + if (hide_options) { + return; + } for (const ResourceImporter::ImportOption &E : options) { - if (ResourceImporterScene::get_singleton()->get_internal_option_visibility(category, E.option.name, current)) { - p_list->push_back(E.option); + if (SceneImportSettings::get_singleton()->is_editing_animation()) { + if (category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX) { + if (ResourceImporterScene::get_animation_singleton()->get_option_visibility(path, E.option.name, current)) { + p_list->push_back(E.option); + } + } else { + if (ResourceImporterScene::get_animation_singleton()->get_internal_option_visibility(category, E.option.name, current)) { + p_list->push_back(E.option); + } + } + } else { + if (category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX) { + if (ResourceImporterScene::get_scene_singleton()->get_option_visibility(path, E.option.name, current)) { + p_list->push_back(E.option); + } + } else { + if (ResourceImporterScene::get_scene_singleton()->get_internal_option_visibility(category, E.option.name, current)) { + p_list->push_back(E.option); + } + } } } } @@ -326,7 +367,9 @@ void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) { } MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(p_node); if (mesh_node && mesh_node->get_mesh().is_valid()) { - _fill_mesh(scene_tree, mesh_node->get_mesh(), item); + if (!editing_animation) { + _fill_mesh(scene_tree, mesh_node->get_mesh(), item); + } // Add the collider view. MeshInstance3D *collider_view = memnew(MeshInstance3D); @@ -365,6 +408,9 @@ void SceneImportSettings::_update_scene() { } void SceneImportSettings::_update_view_gizmos() { + if (!is_visible()) { + return; + } for (const KeyValue<String, NodeData> &e : node_map) { bool generate_collider = false; if (e.value.settings.has(SNAME("generate/physics"))) { @@ -378,6 +424,7 @@ void SceneImportSettings::_update_view_gizmos() { } TypedArray<Node> descendants = mesh_node->find_nodes("collider_view", "MeshInstance3D"); + CRASH_COND_MSG(descendants.is_empty(), "This is unreachable, since the collider view is always created even when the collision is not used! If this is triggered there is a bug on the function `_fill_scene`."); MeshInstance3D *collider_view = static_cast<MeshInstance3D *>(descendants[0].operator Object *()); @@ -460,7 +507,11 @@ void SceneImportSettings::_load_default_subresource_settings(Map<StringName, Var if (d.has(p_import_id)) { d = d[p_import_id]; List<ResourceImporterScene::ImportOption> options; - ResourceImporterScene::get_singleton()->get_internal_import_options(p_category, &options); + if (editing_animation) { + ResourceImporterScene::get_animation_singleton()->get_internal_import_options(p_category, &options); + } else { + ResourceImporterScene::get_scene_singleton()->get_internal_import_options(p_category, &options); + } for (const ResourceImporterScene::ImportOption &E : options) { String key = E.option.name; if (d.has(key)) { @@ -472,21 +523,32 @@ void SceneImportSettings::_load_default_subresource_settings(Map<StringName, Var } void SceneImportSettings::update_view() { - _update_view_gizmos(); + update_view_timer->start(); } -void SceneImportSettings::open_settings(const String &p_path) { +void SceneImportSettings::open_settings(const String &p_path, bool p_for_animation) { if (scene) { memdelete(scene); scene = nullptr; } + + editing_animation = p_for_animation; scene_import_settings_data->settings = nullptr; - scene = ResourceImporterScene::get_singleton()->pre_import(p_path); + scene_import_settings_data->path = p_path; + + scene = ResourceImporterScene::get_scene_singleton()->pre_import(p_path); // Use the scene singleton here because we want to see the full thing. if (scene == nullptr) { EditorNode::get_singleton()->show_warning(TTR("Error opening scene")); return; } + // Visibility + data_mode->set_tab_hidden(1, p_for_animation); + data_mode->set_tab_hidden(2, p_for_animation); + + action_menu->get_popup()->set_item_disabled(action_menu->get_popup()->get_item_id(ACTION_EXTRACT_MATERIALS), p_for_animation); + action_menu->get_popup()->set_item_disabled(action_menu->get_popup()->get_item_id(ACTION_CHOOSE_MESH_SAVE_PATHS), p_for_animation); + base_path = p_path; material_set.clear(); @@ -540,7 +602,11 @@ void SceneImportSettings::open_settings(const String &p_path) { _update_view_gizmos(); _update_camera(); - set_title(vformat(TTR("Advanced Import Settings for '%s'"), base_path.get_file())); + if (p_for_animation) { + set_title(vformat(TTR("Advanced Import Settings for AnimationLibrary '%s'"), base_path.get_file())); + } else { + set_title(vformat(TTR("Advanced Import Settings for Scene '%s'"), base_path.get_file())); + } } SceneImportSettings *SceneImportSettings::singleton = nullptr; @@ -551,6 +617,7 @@ SceneImportSettings *SceneImportSettings::get_singleton() { void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) { selecting = true; + scene_import_settings_data->hide_options = false; if (p_type == "Node") { node_selected->hide(); //always hide just in case @@ -585,10 +652,12 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) { scene_import_settings_data->settings = &nd.settings; if (mi) { scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE; + scene_import_settings_data->hide_options = editing_animation; } else if (Object::cast_to<AnimationPlayer>(nd.node)) { scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE; } else { scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE; + scene_import_settings_data->hide_options = editing_animation; } } } else if (p_type == "Animation") { @@ -671,24 +740,36 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) { List<ResourceImporter::ImportOption> options; - if (scene_import_settings_data->category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX) { - ResourceImporterScene::get_singleton()->get_import_options(base_path, &options); + if (editing_animation) { + if (scene_import_settings_data->category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX) { + ResourceImporterScene::get_animation_singleton()->get_import_options(base_path, &options); + } else { + ResourceImporterScene::get_animation_singleton()->get_internal_import_options(scene_import_settings_data->category, &options); + } + } else { - ResourceImporterScene::get_singleton()->get_internal_import_options(scene_import_settings_data->category, &options); + if (scene_import_settings_data->category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX) { + ResourceImporterScene::get_scene_singleton()->get_import_options(base_path, &options); + } else { + ResourceImporterScene::get_scene_singleton()->get_internal_import_options(scene_import_settings_data->category, &options); + } } scene_import_settings_data->defaults.clear(); scene_import_settings_data->current.clear(); - for (const ResourceImporter::ImportOption &E : options) { - scene_import_settings_data->defaults[E.option.name] = E.default_value; - //needed for visibility toggling (fails if something is missing) - if (scene_import_settings_data->settings->has(E.option.name)) { - scene_import_settings_data->current[E.option.name] = (*scene_import_settings_data->settings)[E.option.name]; - } else { - scene_import_settings_data->current[E.option.name] = E.default_value; + if (scene_import_settings_data->settings) { + for (const ResourceImporter::ImportOption &E : options) { + scene_import_settings_data->defaults[E.option.name] = E.default_value; + //needed for visibility toggling (fails if something is missing) + if (scene_import_settings_data->settings->has(E.option.name)) { + scene_import_settings_data->current[E.option.name] = (*scene_import_settings_data->settings)[E.option.name]; + } else { + scene_import_settings_data->current[E.option.name] = E.default_value; + } } } + scene_import_settings_data->options = options; inspector->edit(scene_import_settings_data); scene_import_settings_data->notify_property_list_changed(); @@ -836,7 +917,7 @@ void SceneImportSettings::_re_import() { main_settings["_subresources"] = subresources; } - EditorFileSystem::get_singleton()->reimport_file_with_custom_parameters(base_path, "scene", main_settings); + EditorFileSystem::get_singleton()->reimport_file_with_custom_parameters(base_path, editing_animation ? "animation_library" : "scene", main_settings); } void SceneImportSettings::_notification(int p_what) { @@ -1282,6 +1363,11 @@ SceneImportSettings::SceneImportSettings() { item_save_path->connect("file_selected", callable_mp(this, &SceneImportSettings::_save_path_changed)); save_path->connect("dir_selected", callable_mp(this, &SceneImportSettings::_save_dir_callback)); + + update_view_timer = memnew(Timer); + update_view_timer->set_wait_time(0.2); + update_view_timer->connect("timeout", callable_mp(this, &SceneImportSettings::_update_view_gizmos)); + add_child(update_view_timer); } SceneImportSettings::~SceneImportSettings() { diff --git a/editor/import/scene_import_settings.h b/editor/import/scene_import_settings.h index 3cf708740b..55cfba3275 100644 --- a/editor/import/scene_import_settings.h +++ b/editor/import/scene_import_settings.h @@ -189,12 +189,17 @@ class SceneImportSettings : public ConfirmationDialog { void _load_default_subresource_settings(Map<StringName, Variant> &settings, const String &p_type, const String &p_import_id, ResourceImporterScene::InternalImportCategory p_category); + bool editing_animation = false; + + Timer *update_view_timer; + protected: void _notification(int p_what); public: + bool is_editing_animation() const { return editing_animation; } void update_view(); - void open_settings(const String &p_path); + void open_settings(const String &p_path, bool p_for_animation = false); static SceneImportSettings *get_singleton(); SceneImportSettings(); ~SceneImportSettings(); diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index 02cc95e14a..755bf7ce07 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -50,8 +50,8 @@ void PluginConfigDialog::_on_confirmed() { String path = "res://addons/" + subfolder_edit->get_text(); if (!_edit_mode) { - DirAccessRef d = DirAccess::create(DirAccess::ACCESS_RESOURCES); - if (!d || d->make_dir_recursive(path) != OK) { + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (d.is_null() || d->make_dir_recursive(path) != OK) { return; } } @@ -137,7 +137,7 @@ void PluginConfigDialog::_on_required_text_changed(const String &) { subfolder_validation->set_texture(invalid_icon); subfolder_validation->set_tooltip(TTR("Subfolder name is not a valid folder name.")); } else { - DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_RESOURCES); String path = "res://addons/" + subfolder_edit->get_text(); if (dir->dir_exists(path) && !_edit_mode) { // Only show this error if in "create" mode. is_valid = false; diff --git a/editor/plugins/animation_library_editor.cpp b/editor/plugins/animation_library_editor.cpp new file mode 100644 index 0000000000..2e9a82a7c2 --- /dev/null +++ b/editor/plugins/animation_library_editor.cpp @@ -0,0 +1,689 @@ +/*************************************************************************/ +/* animation_library_editor.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "animation_library_editor.h" +#include "editor/editor_file_dialog.h" +#include "editor/editor_node.h" +#include "editor/editor_scale.h" + +void AnimationLibraryEditor::set_animation_player(Object *p_player) { + player = p_player; +} + +void AnimationLibraryEditor::_add_library() { + add_library_dialog->set_title(TTR("Library Name:")); + add_library_name->set_text(""); + add_library_dialog->popup_centered(); + add_library_name->grab_focus(); + adding_animation = false; + adding_animation_to_library = StringName(); + _add_library_validate(""); +} + +void AnimationLibraryEditor::_add_library_validate(const String &p_name) { + String error; + + if (adding_animation) { + Ref<AnimationLibrary> al = player->call("get_animation_library", adding_animation_to_library); + ERR_FAIL_COND(al.is_null()); + if (p_name == "") { + error = TTR("Animation name can't be empty."); + + } else if (String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("[")) { + error = TTR("Animation name contains invalid characters: '/', ':', ',' or '['."); + } else if (al->has_animation(p_name)) { + error = TTR("Animation with the same name already exists."); + } + + } else { + if (p_name == "" && bool(player->call("has_animation_library", ""))) { + error = TTR("Enter a library name."); + } else if (String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("[")) { + error = TTR("Library name contains invalid characters: '/', ':', ',' or '['."); + } else if (bool(player->call("has_animation_library", p_name))) { + error = TTR("Library with the same name already exists."); + } + } + + if (error != "") { + add_library_validate->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); + add_library_validate->set_text(error); + add_library_dialog->get_ok_button()->set_disabled(true); + } else { + add_library_validate->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); + if (p_name == "") { + add_library_validate->set_text(TTR("Global library will be created.")); + } else { + add_library_validate->set_text(TTR("Library name is valid.")); + } + add_library_dialog->get_ok_button()->set_disabled(false); + } +} + +void AnimationLibraryEditor::_add_library_confirm() { + if (adding_animation) { + String anim_name = add_library_name->get_text(); + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + Ref<AnimationLibrary> al = player->call("get_animation_library", adding_animation_to_library); + ERR_FAIL_COND(!al.is_valid()); + + Ref<Animation> anim; + anim.instantiate(); + + undo_redo->create_action(vformat(TTR("Add Animation to Library: %s"), anim_name)); + undo_redo->add_do_method(al.ptr(), "add_animation", anim_name, anim); + undo_redo->add_undo_method(al.ptr(), "remove_animation", anim_name); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + undo_redo->commit_action(); + + } else { + String lib_name = add_library_name->get_text(); + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + Ref<AnimationLibrary> al; + al.instantiate(); + + undo_redo->create_action(vformat(TTR("Add Animation Library: %s"), lib_name)); + undo_redo->add_do_method(player, "add_animation_library", lib_name, al); + undo_redo->add_undo_method(player, "remove_animation_library", lib_name); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + undo_redo->commit_action(); + } +} + +void AnimationLibraryEditor::_load_library() { + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("AnimationLibrary", &extensions); + + file_dialog->set_title(TTR("Load Animation")); + file_dialog->clear_filters(); + for (const String &K : extensions) { + file_dialog->add_filter("*." + K); + } + + file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); + file_dialog->set_current_file(""); + file_dialog->popup_centered_ratio(); + + file_dialog_action = FILE_DIALOG_ACTION_OPEN_LIBRARY; +} + +void AnimationLibraryEditor::_file_popup_selected(int p_id) { + Ref<AnimationLibrary> al = player->call("get_animation_library", file_dialog_library); + Ref<Animation> anim; + if (file_dialog_animation != StringName()) { + anim = al->get_animation(file_dialog_animation); + ERR_FAIL_COND(anim.is_null()); + } + switch (p_id) { + case FILE_MENU_SAVE_LIBRARY: { + if (al->get_path().is_resource_file()) { + EditorNode::get_singleton()->save_resource(al); + break; + } + [[fallthrough]]; + } + case FILE_MENU_SAVE_AS_LIBRARY: { + file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); + file_dialog->set_title(TTR("Save Library")); + if (al->get_path().is_resource_file()) { + file_dialog->set_current_path(al->get_path()); + } else { + file_dialog->set_current_file(String(file_dialog_library) + ".res"); + } + file_dialog->clear_filters(); + List<String> exts; + ResourceLoader::get_recognized_extensions_for_type("AnimationLibrary", &exts); + for (const String &K : exts) { + file_dialog->add_filter("*." + K); + } + + file_dialog->popup_centered_ratio(); + file_dialog_action = FILE_DIALOG_ACTION_SAVE_LIBRARY; + } break; + case FILE_MENU_MAKE_LIBRARY_UNIQUE: { + StringName lib_name = file_dialog_library; + + Ref<AnimationLibrary> ald = al->duplicate(); + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo->create_action(vformat(TTR("Make Animation Library Unique: %s"), lib_name)); + undo_redo->add_do_method(player, "remove_animation_library", lib_name); + undo_redo->add_do_method(player, "add_animation_library", lib_name, ald); + undo_redo->add_undo_method(player, "remove_animation_library", lib_name); + undo_redo->add_undo_method(player, "add_animation_library", lib_name, al); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + undo_redo->commit_action(); + + } break; + case FILE_MENU_EDIT_LIBRARY: { + EditorNode::get_singleton()->push_item(al.ptr()); + } break; + + case FILE_MENU_SAVE_ANIMATION: { + if (anim->get_path().is_resource_file()) { + EditorNode::get_singleton()->save_resource(anim); + break; + } + [[fallthrough]]; + } + case FILE_MENU_SAVE_AS_ANIMATION: { + file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); + file_dialog->set_title(TTR("Save Animation")); + if (anim->get_path().is_resource_file()) { + file_dialog->set_current_path(anim->get_path()); + } else { + file_dialog->set_current_file(String(file_dialog_animation) + ".res"); + } + file_dialog->clear_filters(); + List<String> exts; + ResourceLoader::get_recognized_extensions_for_type("Animation", &exts); + for (const String &K : exts) { + file_dialog->add_filter("*." + K); + } + + file_dialog->popup_centered_ratio(); + file_dialog_action = FILE_DIALOG_ACTION_SAVE_ANIMATION; + } break; + case FILE_MENU_MAKE_ANIMATION_UNIQUE: { + StringName anim_name = file_dialog_animation; + + Ref<Animation> animd = anim->duplicate(); + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo->create_action(vformat(TTR("Make Animation Unique: %s"), anim_name)); + undo_redo->add_do_method(al.ptr(), "remove_animation", anim_name); + undo_redo->add_do_method(al.ptr(), "add_animation", anim_name, animd); + undo_redo->add_undo_method(al.ptr(), "remove_animation", anim_name); + undo_redo->add_undo_method(al.ptr(), "add_animation", anim_name, anim); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + undo_redo->commit_action(); + } break; + case FILE_MENU_EDIT_ANIMATION: { + EditorNode::get_singleton()->push_item(anim.ptr()); + } break; + } +} +void AnimationLibraryEditor::_load_file(String p_path) { + switch (file_dialog_action) { + case FILE_DIALOG_ACTION_OPEN_LIBRARY: { + Ref<AnimationLibrary> al = ResourceLoader::load(p_path); + if (al.is_null()) { + error_dialog->set_text(TTR("Invalid AnimationLibrary file.")); + error_dialog->popup_centered(); + return; + } + + TypedArray<StringName> libs = player->call("get_animation_library_list"); + for (int i = 0; i < libs.size(); i++) { + const StringName K = libs[i]; + Ref<AnimationLibrary> al2 = player->call("get_animation_library", K); + if (al2 == al) { + error_dialog->set_text(TTR("This library is already added to the player.")); + error_dialog->popup_centered(); + + return; + } + } + + String name = p_path.get_file().get_basename(); + + int attempt = 1; + + while (bool(player->call("has_animation_library", name))) { + attempt++; + name = p_path.get_file().get_basename() + " " + itos(attempt); + } + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + undo_redo->create_action(vformat(TTR("Add Animation Library: %s"), name)); + undo_redo->add_do_method(player, "add_animation_library", name, al); + undo_redo->add_undo_method(player, "remove_animation_library", name); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + undo_redo->commit_action(); + } break; + case FILE_DIALOG_ACTION_OPEN_ANIMATION: { + Ref<Animation> anim = ResourceLoader::load(p_path); + if (anim.is_null()) { + error_dialog->set_text(TTR("Invalid Animation file.")); + error_dialog->popup_centered(); + return; + } + + Ref<AnimationLibrary> al = player->call("get_animation_library", adding_animation_to_library); + List<StringName> anims; + al->get_animation_list(&anims); + for (const StringName &K : anims) { + Ref<Animation> a2 = al->get_animation(K); + if (a2 == anim) { + error_dialog->set_text(TTR("This animation is already added to the library.")); + error_dialog->popup_centered(); + return; + } + } + + String name = p_path.get_file().get_basename(); + + int attempt = 1; + + while (al->has_animation(name)) { + attempt++; + name = p_path.get_file().get_basename() + " " + itos(attempt); + } + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + undo_redo->create_action(vformat(TTR("Load Animation into Library: %s"), name)); + undo_redo->add_do_method(al.ptr(), "add_animation", name, anim); + undo_redo->add_undo_method(al.ptr(), "remove_animation", name); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + undo_redo->commit_action(); + } break; + + case FILE_DIALOG_ACTION_SAVE_LIBRARY: { + Ref<AnimationLibrary> al = player->call("get_animation_library", file_dialog_library); + String prev_path = al->get_path(); + EditorNode::get_singleton()->save_resource_in_path(al, p_path); + + if (al->get_path() != prev_path) { // Save successful. + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + undo_redo->create_action(vformat(TTR("Save Animation library to File: %s"), file_dialog_library)); + undo_redo->add_do_method(al.ptr(), "set_path", al->get_path()); + undo_redo->add_undo_method(al.ptr(), "set_path", prev_path); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + undo_redo->commit_action(); + } + + } break; + case FILE_DIALOG_ACTION_SAVE_ANIMATION: { + Ref<AnimationLibrary> al = player->call("get_animation_library", file_dialog_library); + Ref<Animation> anim; + if (file_dialog_animation != StringName()) { + anim = al->get_animation(file_dialog_animation); + ERR_FAIL_COND(anim.is_null()); + } + String prev_path = anim->get_path(); + EditorNode::get_singleton()->save_resource_in_path(anim, p_path); + if (anim->get_path() != prev_path) { // Save successful. + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + undo_redo->create_action(vformat(TTR("Save Animation to File: %s"), file_dialog_animation)); + undo_redo->add_do_method(anim.ptr(), "set_path", anim->get_path()); + undo_redo->add_undo_method(anim.ptr(), "set_path", prev_path); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + undo_redo->commit_action(); + } + } break; + } +} + +void AnimationLibraryEditor::_item_renamed() { + TreeItem *ti = tree->get_edited(); + String text = ti->get_text(0); + String old_text = ti->get_metadata(0); + bool restore_text = false; + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + if (String(text).contains("/") || String(text).contains(":") || String(text).contains(",") || String(text).contains("[")) { + restore_text = true; + } else { + if (ti->get_parent() == tree->get_root()) { + // Renamed library + + if (player->call("has_animation_library", text)) { + restore_text = true; + } else { + undo_redo->create_action(vformat(TTR("Rename Animation Library: %s"), text)); + undo_redo->add_do_method(player, "rename_animation_library", old_text, text); + undo_redo->add_undo_method(player, "rename_animation_library", text, old_text); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + updating = true; + undo_redo->commit_action(); + updating = false; + ti->set_metadata(0, text); + if (text == "") { + ti->set_suffix(0, TTR("[Global]")); + } else { + ti->set_suffix(0, ""); + } + } + } else { + // Renamed anim + StringName library = ti->get_parent()->get_metadata(0); + Ref<AnimationLibrary> al = player->call("get_animation_library", library); + + if (al.is_valid()) { + if (al->has_animation(text)) { + restore_text = true; + } else { + undo_redo->create_action(vformat(TTR("Rename Animation: %s"), text)); + undo_redo->add_do_method(al.ptr(), "rename_animation", old_text, text); + undo_redo->add_undo_method(al.ptr(), "rename_animation", text, old_text); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + updating = true; + undo_redo->commit_action(); + updating = false; + + ti->set_metadata(0, text); + } + } else { + restore_text = true; + } + } + } + + if (restore_text) { + ti->set_text(0, old_text); + } +} + +void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int p_button) { + if (p_item->get_parent() == tree->get_root()) { + // Library + StringName lib_name = p_item->get_metadata(0); + Ref<AnimationLibrary> al = player->call("get_animation_library", lib_name); + switch (p_button) { + case LIB_BUTTON_ADD: { + add_library_dialog->set_title(TTR("Animation Name:")); + add_library_name->set_text(""); + add_library_dialog->popup_centered(); + add_library_name->grab_focus(); + adding_animation = true; + adding_animation_to_library = p_item->get_metadata(0); + _add_library_validate(""); + } break; + case LIB_BUTTON_LOAD: { + adding_animation_to_library = p_item->get_metadata(0); + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Animation", &extensions); + + file_dialog->clear_filters(); + for (const String &K : extensions) { + file_dialog->add_filter("*." + K); + } + + file_dialog->set_title(TTR("Load Animation")); + file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); + file_dialog->set_current_file(""); + file_dialog->popup_centered_ratio(); + + file_dialog_action = FILE_DIALOG_ACTION_OPEN_ANIMATION; + + } break; + case LIB_BUTTON_PASTE: { + Ref<Animation> anim = EditorSettings::get_singleton()->get_resource_clipboard(); + if (!anim.is_valid()) { + error_dialog->set_text(TTR("No animation resource in clipboard!")); + error_dialog->popup_centered(); + return; + } + + anim = anim->duplicate(); // Users simply dont care about referencing, so making a copy works better here. + + String base_name; + if (anim->get_name() != "") { + base_name = anim->get_name(); + } else { + base_name = TTR("Pasted Animation"); + } + + String name = base_name; + int attempt = 1; + while (al->has_animation(name)) { + attempt++; + name = base_name + " " + itos(attempt); + } + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + undo_redo->create_action(vformat(TTR("Add Animation to Library: %s"), name)); + undo_redo->add_do_method(al.ptr(), "add_animation", name, anim); + undo_redo->add_undo_method(al.ptr(), "remove_animation", name); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + undo_redo->commit_action(); + + } break; + case LIB_BUTTON_FILE: { + file_popup->clear(); + file_popup->add_item(TTR("Save"), FILE_MENU_SAVE_LIBRARY); + file_popup->add_item(TTR("Save As"), FILE_MENU_SAVE_AS_LIBRARY); + file_popup->add_separator(); + file_popup->add_item(TTR("Make Unique"), FILE_MENU_MAKE_LIBRARY_UNIQUE); + file_popup->add_separator(); + file_popup->add_item(TTR("Open in Inspector"), FILE_MENU_EDIT_LIBRARY); + Rect2 pos = tree->get_item_rect(p_item, 1, 0); + Vector2 popup_pos = tree->get_screen_position() + pos.position + Vector2(0, pos.size.height); + file_popup->popup(Rect2(popup_pos, Size2())); + + file_dialog_animation = StringName(); + file_dialog_library = lib_name; + } break; + case LIB_BUTTON_DELETE: { + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo->create_action(vformat(TTR("Remove Animation Library: %s"), lib_name)); + undo_redo->add_do_method(player, "remove_animation_library", lib_name); + undo_redo->add_undo_method(player, "add_animation_library", lib_name, al); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + undo_redo->commit_action(); + } break; + } + + } else { + // Animation + StringName lib_name = p_item->get_parent()->get_metadata(0); + StringName anim_name = p_item->get_metadata(0); + Ref<AnimationLibrary> al = player->call("get_animation_library", lib_name); + Ref<Animation> anim = al->get_animation(anim_name); + ERR_FAIL_COND(!anim.is_valid()); + switch (p_button) { + case ANIM_BUTTON_COPY: { + if (anim->get_name() == "") { + anim->set_name(anim_name); // Keep the name around + } + EditorSettings::get_singleton()->set_resource_clipboard(anim); + } break; + case ANIM_BUTTON_FILE: { + file_popup->clear(); + file_popup->add_item(TTR("Save"), FILE_MENU_SAVE_ANIMATION); + file_popup->add_item(TTR("Save As"), FILE_MENU_SAVE_AS_ANIMATION); + file_popup->add_separator(); + file_popup->add_item(TTR("Make Unique"), FILE_MENU_MAKE_ANIMATION_UNIQUE); + file_popup->add_separator(); + file_popup->add_item(TTR("Open in Inspector"), FILE_MENU_EDIT_ANIMATION); + Rect2 pos = tree->get_item_rect(p_item, 1, 0); + Vector2 popup_pos = tree->get_screen_position() + pos.position + Vector2(0, pos.size.height); + file_popup->popup(Rect2(popup_pos, Size2())); + + file_dialog_animation = anim_name; + file_dialog_library = lib_name; + + } break; + case ANIM_BUTTON_DELETE: { + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo->create_action(vformat(TTR("Remove Animation from Library: %s"), anim_name)); + undo_redo->add_do_method(al.ptr(), "remove_animation", anim_name); + undo_redo->add_undo_method(al.ptr(), "add_animation", anim_name, anim); + undo_redo->add_do_method(this, "_update_editor", player); + undo_redo->add_undo_method(this, "_update_editor", player); + undo_redo->commit_action(); + } break; + } + } +} + +void AnimationLibraryEditor::update_tree() { + if (updating) { + return; + } + + tree->clear(); + ERR_FAIL_COND(!player); + + Color ss_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor")); + + TreeItem *root = tree->create_item(); + TypedArray<StringName> libs = player->call("get_animation_library_list"); + + for (int i = 0; i < libs.size(); i++) { + const StringName K = libs[i]; + TreeItem *libitem = tree->create_item(root); + libitem->set_text(0, K); + if (K == StringName()) { + libitem->set_suffix(0, TTR("[Global]")); + } else { + libitem->set_suffix(0, ""); + } + libitem->set_editable(0, true); + libitem->set_metadata(0, K); + libitem->set_icon(0, get_theme_icon("AnimationLibrary", "EditorIcons")); + libitem->add_button(0, get_theme_icon("Add", "EditorIcons"), LIB_BUTTON_ADD, false, TTR("Add Animation to Library")); + libitem->add_button(0, get_theme_icon("Load", "EditorIcons"), LIB_BUTTON_LOAD, false, TTR("Load animation from file and add to library")); + libitem->add_button(0, get_theme_icon("ActionPaste", "EditorIcons"), LIB_BUTTON_PASTE, false, TTR("Paste Animation to Library from clipboard")); + Ref<AnimationLibrary> al = player->call("get_animation_library", K); + if (al->get_path().is_resource_file()) { + libitem->set_text(1, al->get_path().get_file()); + libitem->set_tooltip(1, al->get_path()); + } else { + libitem->set_text(1, TTR("[built-in]")); + } + libitem->add_button(1, get_theme_icon("Save", "EditorIcons"), LIB_BUTTON_FILE, false, TTR("Save animation library to resource on disk")); + libitem->add_button(1, get_theme_icon("Remove", "EditorIcons"), LIB_BUTTON_DELETE, false, TTR("Remove animation library")); + + libitem->set_custom_bg_color(0, ss_color); + + List<StringName> animations; + al->get_animation_list(&animations); + for (const StringName &L : animations) { + TreeItem *anitem = tree->create_item(libitem); + anitem->set_text(0, L); + anitem->set_editable(0, true); + anitem->set_metadata(0, L); + anitem->set_icon(0, get_theme_icon("Animation", "EditorIcons")); + anitem->add_button(0, get_theme_icon("ActionCopy", "EditorIcons"), ANIM_BUTTON_COPY, false, TTR("Copy animation to clipboard")); + Ref<Animation> anim = al->get_animation(L); + + if (anim->get_path().is_resource_file()) { + anitem->set_text(1, anim->get_path().get_file()); + anitem->set_tooltip(1, anim->get_path()); + } else { + anitem->set_text(1, TTR("[built-in]")); + } + anitem->add_button(1, get_theme_icon("Save", "EditorIcons"), ANIM_BUTTON_FILE, false, TTR("Save animation to resource on disk")); + anitem->add_button(1, get_theme_icon("Remove", "EditorIcons"), ANIM_BUTTON_DELETE, false, TTR("Remove animation from Library")); + } + } +} + +void AnimationLibraryEditor::show_dialog() { + update_tree(); + popup_centered_ratio(0.5); +} + +void AnimationLibraryEditor::_update_editor(Object *p_player) { + emit_signal("update_editor", p_player); +} + +void AnimationLibraryEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("_update_editor", "player"), &AnimationLibraryEditor::_update_editor); + ADD_SIGNAL(MethodInfo("update_editor")); +} + +AnimationLibraryEditor::AnimationLibraryEditor() { + set_title(TTR("Edit Animation Libraries")); + + file_dialog = memnew(EditorFileDialog); + add_child(file_dialog); + file_dialog->connect("file_selected", callable_mp(this, &AnimationLibraryEditor::_load_file)); + + add_library_dialog = memnew(ConfirmationDialog); + VBoxContainer *dialog_vb = memnew(VBoxContainer); + add_library_name = memnew(LineEdit); + dialog_vb->add_child(add_library_name); + add_library_name->connect("text_changed", callable_mp(this, &AnimationLibraryEditor::_add_library_validate)); + add_child(add_library_dialog); + + add_library_validate = memnew(Label); + dialog_vb->add_child(add_library_validate); + add_library_dialog->add_child(dialog_vb); + add_library_dialog->connect("confirmed", callable_mp(this, &AnimationLibraryEditor::_add_library_confirm)); + add_library_dialog->register_text_enter(add_library_name); + + VBoxContainer *vb = memnew(VBoxContainer); + HBoxContainer *hb = memnew(HBoxContainer); + hb->add_spacer(true); + Button *b = memnew(Button(TTR("Add Library"))); + b->connect("pressed", callable_mp(this, &AnimationLibraryEditor::_add_library)); + hb->add_child(b); + b = memnew(Button(TTR("Load Library"))); + b->connect("pressed", callable_mp(this, &AnimationLibraryEditor::_load_library)); + hb->add_child(b); + vb->add_child(hb); + tree = memnew(Tree); + vb->add_child(tree); + + tree->set_columns(2); + tree->set_column_titles_visible(true); + tree->set_column_title(0, TTR("Resource")); + tree->set_column_title(1, TTR("Storage")); + tree->set_column_expand(0, true); + tree->set_column_custom_minimum_width(1, EDSCALE * 250); + tree->set_column_expand(1, false); + tree->set_hide_root(true); + tree->set_hide_folding(true); + tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); + + tree->connect("item_edited", callable_mp(this, &AnimationLibraryEditor::_item_renamed)); + tree->connect("button_pressed", callable_mp(this, &AnimationLibraryEditor::_button_pressed)); + + file_popup = memnew(PopupMenu); + add_child(file_popup); + file_popup->connect("id_pressed", callable_mp(this, &AnimationLibraryEditor::_file_popup_selected)); + + add_child(vb); + + error_dialog = memnew(AcceptDialog); + error_dialog->set_title(TTR("Error:")); + add_child(error_dialog); +} diff --git a/editor/plugins/animation_library_editor.h b/editor/plugins/animation_library_editor.h new file mode 100644 index 0000000000..5bd4e8d9e2 --- /dev/null +++ b/editor/plugins/animation_library_editor.h @@ -0,0 +1,119 @@ +/*************************************************************************/ +/* animation_library_editor.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 ANIMATION_LIBRARY_EDITOR_H +#define ANIMATION_LIBRARY_EDITOR_H + +#include "editor/animation_track_editor.h" +#include "editor/editor_plugin.h" +#include "scene/animation/animation_player.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/tree.h" + +class EditorFileDialog; + +class AnimationLibraryEditor : public AcceptDialog { + GDCLASS(AnimationLibraryEditor, AcceptDialog) + + enum { + LIB_BUTTON_ADD, + LIB_BUTTON_LOAD, + LIB_BUTTON_PASTE, + LIB_BUTTON_FILE, + LIB_BUTTON_DELETE, + }; + enum { + ANIM_BUTTON_COPY, + ANIM_BUTTON_FILE, + ANIM_BUTTON_DELETE, + }; + + enum FileMenuAction { + FILE_MENU_SAVE_LIBRARY, + FILE_MENU_SAVE_AS_LIBRARY, + FILE_MENU_MAKE_LIBRARY_UNIQUE, + FILE_MENU_EDIT_LIBRARY, + + FILE_MENU_SAVE_ANIMATION, + FILE_MENU_SAVE_AS_ANIMATION, + FILE_MENU_MAKE_ANIMATION_UNIQUE, + FILE_MENU_EDIT_ANIMATION, + }; + + enum FileDialogAction { + FILE_DIALOG_ACTION_OPEN_LIBRARY, + FILE_DIALOG_ACTION_SAVE_LIBRARY, + FILE_DIALOG_ACTION_OPEN_ANIMATION, + FILE_DIALOG_ACTION_SAVE_ANIMATION, + }; + + FileDialogAction file_dialog_action = FILE_DIALOG_ACTION_OPEN_ANIMATION; + + StringName file_dialog_animation; + StringName file_dialog_library; + + AcceptDialog *error_dialog = nullptr; + bool adding_animation = false; + StringName adding_animation_to_library; + EditorFileDialog *file_dialog = nullptr; + ConfirmationDialog *add_library_dialog = nullptr; + LineEdit *add_library_name = nullptr; + Label *add_library_validate = nullptr; + PopupMenu *file_popup = nullptr; + + Tree *tree = nullptr; + + Object *player = nullptr; + + void _add_library(); + void _add_library_validate(const String &p_name); + void _add_library_confirm(); + void _load_library(); + void _load_file(String p_path); + + void _item_renamed(); + void _button_pressed(TreeItem *p_item, int p_column, int p_button); + + void _file_popup_selected(int p_id); + + bool updating = false; + +protected: + void _update_editor(Object *p_player); + static void _bind_methods(); + +public: + void set_animation_player(Object *p_player); + void show_dialog(); + void update_tree(); + AnimationLibraryEditor(); +}; + +#endif // ANIMATIONPLAYERLIBRARYEDITOR_H diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index af7c092d03..17a1bd1048 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -47,6 +47,8 @@ #include "scene/scene_string_names.h" #include "servers/rendering_server.h" +/////////////////////////////////// + void AnimationPlayerEditor::_node_removed(Node *p_node) { if (player && player == p_node) { player = nullptr; @@ -148,9 +150,7 @@ void AnimationPlayerEditor::_notification(int p_what) { #define ITEM_ICON(m_item, m_icon) tool_anim->get_popup()->set_item_icon(tool_anim->get_popup()->get_item_index(m_item), get_theme_icon(SNAME(m_icon), SNAME("EditorIcons"))) ITEM_ICON(TOOL_NEW_ANIM, "New"); - ITEM_ICON(TOOL_LOAD_ANIM, "Load"); - ITEM_ICON(TOOL_SAVE_ANIM, "Save"); - ITEM_ICON(TOOL_SAVE_AS_ANIM, "Save"); + ITEM_ICON(TOOL_ANIM_LIBRARY, "AnimationLibrary"); ITEM_ICON(TOOL_DUPLICATE_ANIM, "Duplicate"); ITEM_ICON(TOOL_RENAME_ANIM, "Rename"); ITEM_ICON(TOOL_EDIT_TRANSITIONS, "Blend"); @@ -166,7 +166,7 @@ void AnimationPlayerEditor::_autoplay_pressed() { if (updating) { return; } - if (animation->get_item_count() == 0) { + if (animation->has_selectable_items() == 0) { return; } @@ -192,10 +192,7 @@ void AnimationPlayerEditor::_autoplay_pressed() { } void AnimationPlayerEditor::_play_pressed() { - String current; - if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) { - current = animation->get_item_text(animation->get_selected()); - } + String current = _get_current(); if (!current.is_empty()) { if (current == player->get_assigned_animation()) { @@ -209,10 +206,7 @@ void AnimationPlayerEditor::_play_pressed() { } void AnimationPlayerEditor::_play_from_pressed() { - String current; - if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) { - current = animation->get_item_text(animation->get_selected()); - } + String current = _get_current(); if (!current.is_empty()) { float time = player->get_current_animation_position(); @@ -229,12 +223,15 @@ void AnimationPlayerEditor::_play_from_pressed() { stop->set_pressed(false); } -void AnimationPlayerEditor::_play_bw_pressed() { +String AnimationPlayerEditor::_get_current() const { String current; - if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) { + if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count() && !animation->is_item_separator(animation->get_selected())) { current = animation->get_item_text(animation->get_selected()); } - + return current; +} +void AnimationPlayerEditor::_play_bw_pressed() { + String current = _get_current(); if (!current.is_empty()) { if (current == player->get_assigned_animation()) { player->stop(); //so it won't blend with itself @@ -247,10 +244,7 @@ void AnimationPlayerEditor::_play_bw_pressed() { } void AnimationPlayerEditor::_play_bw_from_pressed() { - String current; - if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) { - current = animation->get_item_text(animation->get_selected()); - } + String current = _get_current(); if (!current.is_empty()) { float time = player->get_current_animation_position(); @@ -282,10 +276,7 @@ void AnimationPlayerEditor::_animation_selected(int p_which) { } // when selecting an animation, the idea is that the only interesting behavior // ui-wise is that it should play/blend the next one if currently playing - String current; - if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) { - current = animation->get_item_text(animation->get_selected()); - } + String current = _get_current(); if (!current.is_empty()) { player->set_assigned_animation(current); @@ -330,6 +321,20 @@ void AnimationPlayerEditor::_animation_new() { break; } + List<StringName> libraries; + player->get_animation_library_list(&libraries); + library->clear(); + for (const StringName &K : libraries) { + library->add_item((K == StringName()) ? String(TTR("[Global]")) : String(K)); + library->set_item_metadata(0, String(K)); + } + + if (libraries.size() > 1) { + library->show(); + } else { + library->hide(); + } + name->set_text(base); name_dialog->popup_centered(Size2(300, 90)); name->select_all(); @@ -337,7 +342,7 @@ void AnimationPlayerEditor::_animation_new() { } void AnimationPlayerEditor::_animation_rename() { - if (animation->get_item_count() == 0) { + if (!animation->has_selectable_items()) { return; } int selected = animation->get_selected(); @@ -349,84 +354,11 @@ void AnimationPlayerEditor::_animation_rename() { name_dialog->popup_centered(Size2(300, 90)); name->select_all(); name->grab_focus(); -} - -void AnimationPlayerEditor::_animation_load() { - ERR_FAIL_COND(!player); - file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILES); - file->clear_filters(); - List<String> extensions; - - ResourceLoader::get_recognized_extensions_for_type("Animation", &extensions); - for (const String &E : extensions) { - file->add_filter("*." + E + " ; " + E.to_upper()); - } - - file->popup_file_dialog(); -} - -void AnimationPlayerEditor::_animation_save_in_path(const Ref<Resource> &p_resource, const String &p_path) { - int flg = 0; - if (EditorSettings::get_singleton()->get("filesystem/on_save/compress_binary_resources")) { - flg |= ResourceSaver::FLAG_COMPRESS; - } - - String path = ProjectSettings::get_singleton()->localize_path(p_path); - Error err = ResourceSaver::save(path, p_resource, flg | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS); - - if (err != OK) { - EditorNode::get_singleton()->show_warning(TTR("Error saving resource!")); - return; - } - - ((Resource *)p_resource.ptr())->set_path(path); - EditorNode::get_singleton()->emit_signal(SNAME("resource_saved"), p_resource); -} - -void AnimationPlayerEditor::_animation_save(const Ref<Resource> &p_resource) { - if (p_resource->get_path().is_resource_file()) { - _animation_save_in_path(p_resource, p_resource->get_path()); - } else { - _animation_save_as(p_resource); - } -} - -void AnimationPlayerEditor::_animation_save_as(const Ref<Resource> &p_resource) { - file->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); - - List<String> extensions; - ResourceSaver::get_recognized_extensions(p_resource, &extensions); - file->clear_filters(); - for (int i = 0; i < extensions.size(); i++) { - file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); - } - - String path; - //file->set_current_path(current_path); - if (!p_resource->get_path().is_empty()) { - path = p_resource->get_path(); - if (extensions.size()) { - if (extensions.find(p_resource->get_path().get_extension().to_lower()) == nullptr) { - path = p_resource->get_path().get_base_dir() + p_resource->get_name() + "." + extensions.front()->get(); - } - } - } else { - if (extensions.size()) { - if (!p_resource->get_name().is_empty()) { - path = p_resource->get_name() + "." + extensions.front()->get().to_lower(); - } else { - String resource_name_snake_case = p_resource->get_class().camelcase_to_underscore(); - path = "new_" + resource_name_snake_case + "." + extensions.front()->get().to_lower(); - } - } - } - file->set_current_path(path); - file->set_title(TTR("Save Resource As...")); - file->popup_file_dialog(); + library->hide(); } void AnimationPlayerEditor::_animation_remove() { - if (animation->get_item_count() == 0) { + if (!animation->has_selectable_items()) { return; } @@ -440,6 +372,9 @@ void AnimationPlayerEditor::_animation_remove_confirmed() { String current = animation->get_item_text(animation->get_selected()); Ref<Animation> anim = player->get_animation(current); + Ref<AnimationLibrary> al = player->get_animation_library(player->find_animation_library(anim)); + ERR_FAIL_COND(al.is_null()); + undo_redo->create_action(TTR("Remove Animation")); if (player->get_autoplay() == current) { undo_redo->add_do_method(player, "set_autoplay", ""); @@ -447,11 +382,11 @@ void AnimationPlayerEditor::_animation_remove_confirmed() { // Avoid having the autoplay icon linger around if there is only one animation in the player. undo_redo->add_do_method(this, "_animation_player_changed", player); } - undo_redo->add_do_method(player, "remove_animation", current); - undo_redo->add_undo_method(player, "add_animation", current, anim); + undo_redo->add_do_method(al.ptr(), "remove_animation", current); + undo_redo->add_undo_method(al.ptr(), "add_animation", current, anim); undo_redo->add_do_method(this, "_animation_player_changed", player); undo_redo->add_undo_method(this, "_animation_player_changed", player); - if (animation->get_item_count() == 1) { + if (animation->has_selectable_items() && animation->get_selectable_item(false) == animation->get_selectable_item(true)) { // Last item remaining. undo_redo->add_do_method(this, "_stop_onion_skinning"); undo_redo->add_undo_method(this, "_start_onion_skinning"); } @@ -498,7 +433,7 @@ void AnimationPlayerEditor::_animation_name_edited() { return; } - if (name_dialog_op == TOOL_RENAME_ANIM && animation->get_item_count() > 0 && animation->get_item_text(animation->get_selected()) == new_name) { + if (name_dialog_op == TOOL_RENAME_ANIM && animation->has_selectable_items() && animation->get_item_text(animation->get_selected()) == new_name) { name_dialog->hide(); return; } @@ -514,10 +449,13 @@ void AnimationPlayerEditor::_animation_name_edited() { String current = animation->get_item_text(animation->get_selected()); Ref<Animation> anim = player->get_animation(current); + Ref<AnimationLibrary> al = player->get_animation_library(player->find_animation_library(anim)); + ERR_FAIL_COND(al.is_null()); + undo_redo->create_action(TTR("Rename Animation")); - undo_redo->add_do_method(player, "rename_animation", current, new_name); + undo_redo->add_do_method(al.ptr(), "rename_animation", current, new_name); undo_redo->add_do_method(anim.ptr(), "set_name", new_name); - undo_redo->add_undo_method(player, "rename_animation", new_name, current); + undo_redo->add_undo_method(al.ptr(), "rename_animation", new_name, current); undo_redo->add_undo_method(anim.ptr(), "set_name", current); undo_redo->add_do_method(this, "_animation_player_changed", player); undo_redo->add_undo_method(this, "_animation_player_changed", player); @@ -530,15 +468,35 @@ void AnimationPlayerEditor::_animation_name_edited() { Ref<Animation> new_anim = Ref<Animation>(memnew(Animation)); new_anim->set_name(new_name); + Ref<AnimationLibrary> al; + if (library->is_visible()) { + al = player->get_animation_library(library->get_item_metadata(library->get_selected())); + } else { + if (player->has_animation_library("")) { + al = player->get_animation_library(""); + } + } + undo_redo->create_action(TTR("Add Animation")); - undo_redo->add_do_method(player, "add_animation", new_name, new_anim); - undo_redo->add_undo_method(player, "remove_animation", new_name); + + bool lib_added = false; + if (al.is_null()) { + al.instantiate(); + lib_added = true; + undo_redo->add_do_method(player, "add_animation_library", "", al); + } + + undo_redo->add_do_method(al.ptr(), "add_animation", new_name, new_anim); + undo_redo->add_undo_method(al.ptr(), "remove_animation", new_name); undo_redo->add_do_method(this, "_animation_player_changed", player); undo_redo->add_undo_method(this, "_animation_player_changed", player); - if (animation->get_item_count() == 0) { + if (!animation->has_selectable_items()) { undo_redo->add_do_method(this, "_start_onion_skinning"); undo_redo->add_undo_method(this, "_stop_onion_skinning"); } + if (lib_added) { + undo_redo->add_undo_method(player, "remove_animation_library", ""); + } undo_redo->commit_action(); _select_anim_by_name(new_name); @@ -551,9 +509,11 @@ void AnimationPlayerEditor::_animation_name_edited() { Ref<Animation> new_anim = _animation_clone(anim); new_anim->set_name(new_name); + Ref<AnimationLibrary> library = player->get_animation_library(player->find_animation_library(anim)); + undo_redo->create_action(TTR("Duplicate Animation")); - undo_redo->add_do_method(player, "add_animation", new_name, new_anim); - undo_redo->add_undo_method(player, "remove_animation", new_name); + undo_redo->add_do_method(library.ptr(), "add_animation", new_name, new_anim); + undo_redo->add_undo_method(library.ptr(), "remove_animation", new_name); undo_redo->add_do_method(player, "animation_set_next", new_name, player->animation_get_next(current)); undo_redo->add_do_method(this, "_animation_player_changed", player); undo_redo->add_undo_method(this, "_animation_player_changed", player); @@ -567,7 +527,7 @@ void AnimationPlayerEditor::_animation_name_edited() { } void AnimationPlayerEditor::_blend_editor_next_changed(const int p_idx) { - if (animation->get_item_count() == 0) { + if (!animation->has_selectable_items()) { return; } @@ -588,7 +548,7 @@ void AnimationPlayerEditor::_animation_blend() { blend_editor.tree->clear(); - if (animation->get_item_count() == 0) { + if (!animation->has_selectable_items()) { return; } @@ -643,7 +603,7 @@ void AnimationPlayerEditor::_blend_edited() { return; } - if (animation->get_item_count() == 0) { + if (!animation->has_selectable_items()) { return; } @@ -722,16 +682,16 @@ void AnimationPlayerEditor::set_state(const Dictionary &p_state) { } void AnimationPlayerEditor::_animation_resource_edit() { - if (animation->get_item_count()) { - String current = animation->get_item_text(animation->get_selected()); + String current = _get_current(); + if (current != String()) { Ref<Animation> anim = player->get_animation(current); EditorNode::get_singleton()->edit_resource(anim); } } void AnimationPlayerEditor::_animation_edit() { - if (animation->get_item_count()) { - String current = animation->get_item_text(animation->get_selected()); + String current = _get_current(); + if (current != String()) { Ref<Animation> anim = player->get_animation(current); track_editor->set_animation(anim); @@ -745,51 +705,6 @@ void AnimationPlayerEditor::_animation_edit() { } } -void AnimationPlayerEditor::_save_animation(String p_file) { - String current = animation->get_item_text(animation->get_selected()); - if (!current.is_empty()) { - Ref<Animation> anim = player->get_animation(current); - - ERR_FAIL_COND(!Object::cast_to<Resource>(*anim)); - - RES current_res = RES(Object::cast_to<Resource>(*anim)); - - _animation_save_in_path(current_res, p_file); - } -} - -void AnimationPlayerEditor::_load_animations(Vector<String> p_files) { - ERR_FAIL_COND(!player); - - for (int i = 0; i < p_files.size(); i++) { - String file = p_files[i]; - - Ref<Resource> res = ResourceLoader::load(file, "Animation"); - ERR_FAIL_COND_MSG(res.is_null(), "Cannot load Animation from file '" + file + "'."); - ERR_FAIL_COND_MSG(!res->is_class("Animation"), "Loaded resource from file '" + file + "' is not Animation."); - if (file.rfind("/") != -1) { - file = file.substr(file.rfind("/") + 1, file.length()); - } - if (file.rfind("\\") != -1) { - file = file.substr(file.rfind("\\") + 1, file.length()); - } - - if (file.contains(".")) { - file = file.substr(0, file.find(".")); - } - - undo_redo->create_action(TTR("Load Animation")); - undo_redo->add_do_method(player, "add_animation", file, res); - undo_redo->add_undo_method(player, "remove_animation", file); - if (player->has_animation(file)) { - undo_redo->add_undo_method(player, "add_animation", file, player->get_animation(file)); - } - undo_redo->add_do_method(this, "_animation_player_changed", player); - undo_redo->add_undo_method(this, "_animation_player_changed", player); - undo_redo->commit_action(); - } -} - void AnimationPlayerEditor::_scale_changed(const String &p_scale) { player->set_speed_scale(p_scale.to_float()); } @@ -824,49 +739,67 @@ void AnimationPlayerEditor::_update_animation() { void AnimationPlayerEditor::_update_player() { updating = true; - List<StringName> animlist; - if (player) { - player->get_animation_list(&animlist); - } animation->clear(); -#define ITEM_DISABLED(m_item, m_disabled) tool_anim->get_popup()->set_item_disabled(tool_anim->get_popup()->get_item_index(m_item), m_disabled) - - ITEM_DISABLED(TOOL_SAVE_ANIM, animlist.size() == 0); - ITEM_DISABLED(TOOL_SAVE_AS_ANIM, animlist.size() == 0); - ITEM_DISABLED(TOOL_DUPLICATE_ANIM, animlist.size() == 0); - ITEM_DISABLED(TOOL_RENAME_ANIM, animlist.size() == 0); - ITEM_DISABLED(TOOL_EDIT_TRANSITIONS, animlist.size() == 0); - ITEM_DISABLED(TOOL_COPY_ANIM, animlist.size() == 0); - ITEM_DISABLED(TOOL_REMOVE_ANIM, animlist.size() == 0); - - stop->set_disabled(animlist.size() == 0); - play->set_disabled(animlist.size() == 0); - play_bw->set_disabled(animlist.size() == 0); - play_bw_from->set_disabled(animlist.size() == 0); - play_from->set_disabled(animlist.size() == 0); - frame->set_editable(animlist.size() != 0); - animation->set_disabled(animlist.size() == 0); - autoplay->set_disabled(animlist.size() == 0); - tool_anim->set_disabled(player == nullptr); - onion_toggle->set_disabled(animlist.size() == 0); - onion_skinning->set_disabled(animlist.size() == 0); - pin->set_disabled(player == nullptr); - if (!player) { AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying(); return; } + List<StringName> libraries; + if (player) { + player->get_animation_library_list(&libraries); + } + int active_idx = -1; - for (const StringName &E : animlist) { - animation->add_item(E); + bool no_anims_found = true; - if (player->get_assigned_animation() == E) { - active_idx = animation->get_item_count() - 1; + for (const StringName &K : libraries) { + if (K != StringName()) { + animation->add_separator(K); + } + + Ref<AnimationLibrary> library = player->get_animation_library(K); + List<StringName> animlist; + library->get_animation_list(&animlist); + + for (const StringName &E : animlist) { + String path = K; + if (path != "") { + path += "/"; + } + path += E; + animation->add_item(path); + if (player->get_assigned_animation() == path) { + active_idx = animation->get_selectable_item(true); + } + no_anims_found = false; } } +#define ITEM_CHECK_DISABLED(m_item) tool_anim->get_popup()->set_item_disabled(tool_anim->get_popup()->get_item_index(m_item), no_anims_found) + + ITEM_CHECK_DISABLED(TOOL_DUPLICATE_ANIM); + ITEM_CHECK_DISABLED(TOOL_RENAME_ANIM); + ITEM_CHECK_DISABLED(TOOL_EDIT_TRANSITIONS); + ITEM_CHECK_DISABLED(TOOL_REMOVE_ANIM); + ITEM_CHECK_DISABLED(TOOL_EDIT_RESOURCE); + +#undef ITEM_CHECK_DISABLED + + stop->set_disabled(no_anims_found); + play->set_disabled(no_anims_found); + play_bw->set_disabled(no_anims_found); + play_bw_from->set_disabled(no_anims_found); + play_from->set_disabled(no_anims_found); + frame->set_editable(!no_anims_found); + animation->set_disabled(no_anims_found); + autoplay->set_disabled(no_anims_found); + tool_anim->set_disabled(player == nullptr); + onion_toggle->set_disabled(no_anims_found); + onion_skinning->set_disabled(no_anims_found); + pin->set_disabled(player == nullptr); + _update_animation_list_icons(); updating = false; @@ -874,16 +807,16 @@ void AnimationPlayerEditor::_update_player() { animation->select(active_idx); autoplay->set_pressed(animation->get_item_text(active_idx) == player->get_autoplay()); _animation_selected(active_idx); - - } else if (animation->get_item_count() > 0) { - animation->select(0); - autoplay->set_pressed(animation->get_item_text(0) == player->get_autoplay()); - _animation_selected(0); + } else if (animation->has_selectable_items()) { + int item = animation->get_selectable_item(); + animation->select(item); + autoplay->set_pressed(animation->get_item_text(item) == player->get_autoplay()); + _animation_selected(item); } else { _animation_selected(0); } - if (animation->get_item_count()) { + if (!no_anims_found) { String current = animation->get_item_text(animation->get_selected()); Ref<Animation> anim = player->get_animation(current); track_editor->set_animation(anim); @@ -899,6 +832,9 @@ void AnimationPlayerEditor::_update_player() { void AnimationPlayerEditor::_update_animation_list_icons() { for (int i = 0; i < animation->get_item_count(); i++) { String name = animation->get_item_text(i); + if (animation->is_item_disabled(i) || animation->is_item_separator(i)) { + continue; + } Ref<Texture2D> icon; if (name == player->get_autoplay()) { @@ -925,7 +861,7 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { _update_player(); if (onion.enabled) { - if (animation->get_item_count() > 0) { + if (animation->has_selectable_items()) { _start_onion_skinning(); } else { _stop_onion_skinning(); @@ -940,6 +876,8 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { track_editor->show_select_node_warning(true); } + + library_editor->set_animation_player(player); } void AnimationPlayerEditor::forward_force_draw_over_viewport(Control *p_overlay) { @@ -993,7 +931,7 @@ void AnimationPlayerEditor::forward_force_draw_over_viewport(Control *p_overlay) } void AnimationPlayerEditor::_animation_duplicate() { - if (!animation->get_item_count()) { + if (!animation->has_selectable_items()) { return; } @@ -1031,29 +969,6 @@ Ref<Animation> AnimationPlayerEditor::_animation_clone(Ref<Animation> p_anim) { return new_anim; } -void AnimationPlayerEditor::_animation_paste(Ref<Animation> p_anim) { - String name = p_anim->get_name(); - if (name.is_empty()) { - name = TTR("Pasted Animation"); - } - - int idx = 1; - String base = name; - while (player->has_animation(name)) { - idx++; - name = base + " " + itos(idx); - } - - undo_redo->create_action(TTR("Paste Animation")); - undo_redo->add_do_method(player, "add_animation", name, p_anim); - undo_redo->add_undo_method(player, "remove_animation", name); - undo_redo->add_do_method(this, "_animation_player_changed", player); - undo_redo->add_undo_method(this, "_animation_player_changed", player); - undo_redo->commit_action(); - - _select_anim_by_name(name); -} - void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set, bool p_timeline_only) { if (updating || !player || player->is_playing()) { return; @@ -1095,6 +1010,9 @@ void AnimationPlayerEditor::_animation_player_changed(Object *p_pl) { if (blend_editor.dialog->is_visible()) { _animation_blend(); // Update. } + if (library_editor->is_visible()) { + library_editor->update_tree(); + } } } @@ -1134,10 +1052,7 @@ void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos, bool p_drag, } void AnimationPlayerEditor::_animation_tool_menu(int p_option) { - String current; - if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) { - current = animation->get_item_text(animation->get_selected()); - } + String current = _get_current(); Ref<Animation> anim; if (!current.is_empty()) { @@ -1148,18 +1063,9 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { case TOOL_NEW_ANIM: { _animation_new(); } break; - case TOOL_LOAD_ANIM: { - _animation_load(); - } break; - case TOOL_SAVE_ANIM: { - if (anim.is_valid()) { - _animation_save(anim); - } - } break; - case TOOL_SAVE_AS_ANIM: { - if (anim.is_valid()) { - _animation_save_as(anim); - } + case TOOL_ANIM_LIBRARY: { + library_editor->set_animation_player(player); + library_editor->show_dialog(); } break; case TOOL_DUPLICATE_ANIM: { _animation_duplicate(); @@ -1173,47 +1079,10 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { case TOOL_REMOVE_ANIM: { _animation_remove(); } break; - case TOOL_COPY_ANIM: { - if (!animation->get_item_count()) { - error_dialog->set_text(TTR("No animation to copy!")); - error_dialog->popup_centered(); - return; - } - - String current2 = animation->get_item_text(animation->get_selected()); - Ref<Animation> anim2 = player->get_animation(current2); - EditorSettings::get_singleton()->set_resource_clipboard(anim2); - } break; - case TOOL_PASTE_ANIM: { - Ref<Animation> anim2 = EditorSettings::get_singleton()->get_resource_clipboard(); - if (!anim2.is_valid()) { - error_dialog->set_text(TTR("No animation resource in clipboard!")); - error_dialog->popup_centered(); - return; - } - Ref<Animation> new_anim = _animation_clone(anim2); - _animation_paste(new_anim); - } break; - case TOOL_PASTE_ANIM_REF: { - Ref<Animation> anim2 = EditorSettings::get_singleton()->get_resource_clipboard(); - if (!anim2.is_valid()) { - error_dialog->set_text(TTR("No animation resource in clipboard!")); - error_dialog->popup_centered(); - return; - } - - _animation_paste(anim2); - } break; case TOOL_EDIT_RESOURCE: { - if (!animation->get_item_count()) { - error_dialog->set_text(TTR("No animation to edit!")); - error_dialog->popup_centered(); - return; + if (anim.is_valid()) { + EditorNode::get_singleton()->edit_resource(anim); } - - String current2 = animation->get_item_text(animation->get_selected()); - Ref<Animation> anim2 = player->get_animation(current2); - EditorNode::get_singleton()->edit_resource(anim2); } break; } } @@ -1300,7 +1169,7 @@ void AnimationPlayerEditor::shortcut_input(const Ref<InputEvent> &p_ev) { } void AnimationPlayerEditor::_editor_visibility_changed() { - if (is_visible() && animation->get_item_count() > 0) { + if (is_visible() && animation->has_selectable_items()) { _start_onion_skinning(); } } @@ -1536,7 +1405,6 @@ void AnimationPlayerEditor::_pin_pressed() { void AnimationPlayerEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_animation_new"), &AnimationPlayerEditor::_animation_new); ClassDB::bind_method(D_METHOD("_animation_rename"), &AnimationPlayerEditor::_animation_rename); - ClassDB::bind_method(D_METHOD("_animation_load"), &AnimationPlayerEditor::_animation_load); ClassDB::bind_method(D_METHOD("_animation_remove"), &AnimationPlayerEditor::_animation_remove); ClassDB::bind_method(D_METHOD("_animation_blend"), &AnimationPlayerEditor::_animation_blend); ClassDB::bind_method(D_METHOD("_animation_edit"), &AnimationPlayerEditor::_animation_edit); @@ -1623,13 +1491,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug tool_anim->set_text(TTR("Animation")); tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/new_animation", TTR("New")), TOOL_NEW_ANIM); tool_anim->get_popup()->add_separator(); - tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/open_animation", TTR("Load")), TOOL_LOAD_ANIM); - tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save_animation", TTR("Save")), TOOL_SAVE_ANIM); - tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save_as_animation", TTR("Save As...")), TOOL_SAVE_AS_ANIM); - tool_anim->get_popup()->add_separator(); - tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/copy_animation", TTR("Copy")), TOOL_COPY_ANIM); - tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/paste_animation", TTR("Paste")), TOOL_PASTE_ANIM); - tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/paste_animation_as_reference", TTR("Paste As Reference")), TOOL_PASTE_ANIM_REF); + tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/animation_libraries", TTR("Manage Animations...")), TOOL_ANIM_LIBRARY); tool_anim->get_popup()->add_separator(); tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/duplicate_animation", TTR("Duplicate...")), TOOL_DUPLICATE_ANIM); tool_anim->get_popup()->add_separator(); @@ -1638,6 +1500,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/open_animation_in_inspector", TTR("Open in Inspector")), TOOL_EDIT_RESOURCE); tool_anim->get_popup()->add_separator(); tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/remove_animation", TTR("Remove")), TOOL_REMOVE_ANIM); + tool_anim->set_disabled(true); hb->add_child(tool_anim); animation = memnew(OptionButton); @@ -1705,8 +1568,14 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug name_title = memnew(Label(TTR("Animation Name:"))); vb->add_child(name_title); + HBoxContainer *name_hb = memnew(HBoxContainer); name = memnew(LineEdit); - vb->add_child(name); + name_hb->add_child(name); + name->set_h_size_flags(SIZE_EXPAND_FILL); + library = memnew(OptionButton); + name_hb->add_child(library); + library->hide(); + vb->add_child(name_hb); name_dialog->register_text_enter(name); error_dialog = memnew(ConfirmationDialog); @@ -1742,8 +1611,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug animation->connect("item_selected", callable_mp(this, &AnimationPlayerEditor::_animation_selected)); - file->connect("file_selected", callable_mp(this, &AnimationPlayerEditor::_save_animation)); - file->connect("files_selected", callable_mp(this, &AnimationPlayerEditor::_load_animations)); frame->connect("value_changed", callable_mp(this, &AnimationPlayerEditor::_seek_value_changed), make_binds(true, false)); scale->connect("text_submitted", callable_mp(this, &AnimationPlayerEditor::_scale_changed)); @@ -1759,6 +1626,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug _update_player(); + library_editor = memnew(AnimationLibraryEditor); + add_child(library_editor); + library_editor->connect("update_editor", callable_mp(this, &AnimationPlayerEditor::_animation_player_changed)); + // Onion skinning. track_editor->connect("visibility_changed", callable_mp(this, &AnimationPlayerEditor::_editor_visibility_changed)); diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 4f6a9c534f..0cc04460ca 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -33,6 +33,7 @@ #include "editor/animation_track_editor.h" #include "editor/editor_plugin.h" +#include "editor/plugins/animation_library_editor.h" #include "scene/animation/animation_player.h" #include "scene/gui/dialogs.h" #include "scene/gui/slider.h" @@ -40,7 +41,6 @@ #include "scene/gui/texture_button.h" #include "scene/gui/tree.h" -class EditorFileDialog; class AnimationPlayerEditorPlugin; class AnimationPlayerEditor : public VBoxContainer { @@ -51,16 +51,11 @@ class AnimationPlayerEditor : public VBoxContainer { enum { TOOL_NEW_ANIM, - TOOL_LOAD_ANIM, - TOOL_SAVE_ANIM, - TOOL_SAVE_AS_ANIM, + TOOL_ANIM_LIBRARY, TOOL_DUPLICATE_ANIM, TOOL_RENAME_ANIM, TOOL_EDIT_TRANSITIONS, TOOL_REMOVE_ANIM, - TOOL_COPY_ANIM, - TOOL_PASTE_ANIM, - TOOL_PASTE_ANIM_REF, TOOL_EDIT_RESOURCE }; @@ -103,8 +98,10 @@ class AnimationPlayerEditor : public VBoxContainer { SpinBox *frame = nullptr; LineEdit *scale = nullptr; LineEdit *name = nullptr; + OptionButton *library = nullptr; Label *name_title = nullptr; UndoRedo *undo_redo = nullptr; + Ref<Texture2D> autoplay_icon; Ref<Texture2D> reset_icon; Ref<ImageTexture> autoplay_reset_icon; @@ -114,6 +111,8 @@ class AnimationPlayerEditor : public VBoxContainer { EditorFileDialog *file = nullptr; ConfirmationDialog *delete_dialog = nullptr; + AnimationLibraryEditor *library_editor = nullptr; + struct BlendEditor { AcceptDialog *dialog = nullptr; Tree *tree = nullptr; @@ -173,11 +172,6 @@ class AnimationPlayerEditor : public VBoxContainer { void _animation_new(); void _animation_rename(); void _animation_name_edited(); - void _animation_load(); - - void _animation_save_in_path(const Ref<Resource> &p_resource, const String &p_path); - void _animation_save(const Ref<Resource> &p_resource); - void _animation_save_as(const Ref<Resource> &p_resource); void _animation_remove(); void _animation_remove_confirmed(); @@ -185,11 +179,8 @@ class AnimationPlayerEditor : public VBoxContainer { void _animation_edit(); void _animation_duplicate(); Ref<Animation> _animation_clone(const Ref<Animation> p_anim); - void _animation_paste(const Ref<Animation> p_anim); void _animation_resource_edit(); void _scale_changed(const String &p_scale); - void _save_animation(String p_file); - void _load_animations(Vector<String> p_files); void _seek_value_changed(float p_value, bool p_set = false, bool p_timeline_only = false); void _blend_editor_next_changed(const int p_idx); @@ -219,6 +210,7 @@ class AnimationPlayerEditor : public VBoxContainer { void _stop_onion_skinning(); void _pin_pressed(); + String _get_current() const; ~AnimationPlayerEditor(); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 405ece1471..157eed02f4 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -729,9 +729,8 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PackedB if (use_cache) { String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text()); - FileAccess *file = FileAccess::open(cache_filename_base + ".data", FileAccess::READ); - - if (file) { + Ref<FileAccess> file = FileAccess::open(cache_filename_base + ".data", FileAccess::READ); + if (file.is_valid()) { PackedByteArray cached_data; int len = file->get_32(); cached_data.resize(len); @@ -740,8 +739,6 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PackedB file->get_buffer(w, len); image_data = cached_data; - file->close(); - memdelete(file); } } @@ -808,23 +805,17 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons if (headers[i].findn("ETag:") == 0) { // Save etag String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text()); String new_etag = headers[i].substr(headers[i].find(":") + 1, headers[i].length()).strip_edges(); - FileAccess *file; - - file = FileAccess::open(cache_filename_base + ".etag", FileAccess::WRITE); - if (file) { + Ref<FileAccess> file = FileAccess::open(cache_filename_base + ".etag", FileAccess::WRITE); + if (file.is_valid()) { file->store_line(new_etag); - file->close(); - memdelete(file); } int len = p_data.size(); const uint8_t *r = p_data.ptr(); file = FileAccess::open(cache_filename_base + ".data", FileAccess::WRITE); - if (file) { + if (file.is_valid()) { file->store_32(len); file->store_buffer(r, len); - file->close(); - memdelete(file); } break; @@ -858,11 +849,9 @@ void EditorAssetLibrary::_update_image_queue() { Vector<String> headers; if (FileAccess::exists(cache_filename_base + ".etag") && FileAccess::exists(cache_filename_base + ".data")) { - FileAccess *file = FileAccess::open(cache_filename_base + ".etag", FileAccess::READ); - if (file) { + Ref<FileAccess> file = FileAccess::open(cache_filename_base + ".etag", FileAccess::READ); + if (file.is_valid()) { headers.push_back("If-None-Match: " + file->get_line()); - file->close(); - memdelete(file); } } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index a90e151adb..8d0db697e2 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -2298,7 +2298,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { } add_node_menu->reset_size(); - add_node_menu->set_position(get_screen_transform().xform(b->get_position())); + add_node_menu->set_position(viewport->get_screen_transform().xform(b->get_position())); add_node_menu->popup(); node_create_position = transform.affine_inverse().xform(b->get_position()); return true; diff --git a/editor/plugins/input_event_editor_plugin.cpp b/editor/plugins/input_event_editor_plugin.cpp index b4a7081ebc..fb0e260388 100644 --- a/editor/plugins/input_event_editor_plugin.cpp +++ b/editor/plugins/input_event_editor_plugin.cpp @@ -33,6 +33,15 @@ void InputEventConfigContainer::_bind_methods() { } +void InputEventConfigContainer::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + open_config_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); + } break; + } +} + void InputEventConfigContainer::_configure_pressed() { config_dialog->popup_and_configure(input_event); } @@ -47,12 +56,6 @@ void InputEventConfigContainer::_config_dialog_confirmed() { _event_changed(); } -Size2 InputEventConfigContainer::get_minimum_size() const { - // Don't bother with a minimum x size for the control - we don't want the inspector - // to jump in size if a long text is placed in the label (e.g. Joypad Axis description) - return Size2(0, HBoxContainer::get_minimum_size().y); -} - void InputEventConfigContainer::set_event(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; Ref<InputEventMouseButton> m = p_event; @@ -75,29 +78,26 @@ void InputEventConfigContainer::set_event(const Ref<InputEvent> &p_event) { } InputEventConfigContainer::InputEventConfigContainer() { - MarginContainer *mc = memnew(MarginContainer); - mc->add_theme_constant_override("margin_left", 10); - mc->add_theme_constant_override("margin_right", 10); - mc->add_theme_constant_override("margin_top", 10); - mc->add_theme_constant_override("margin_bottom", 10); - add_child(mc); - - HBoxContainer *hb = memnew(HBoxContainer); - mc->add_child(hb); + input_event_text = memnew(Label); + input_event_text->set_h_size_flags(SIZE_EXPAND_FILL); + input_event_text->set_autowrap_mode(Label::AutowrapMode::AUTOWRAP_WORD_SMART); + input_event_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + add_child(input_event_text); open_config_button = memnew(Button); open_config_button->set_text(TTR("Configure")); open_config_button->connect("pressed", callable_mp(this, &InputEventConfigContainer::_configure_pressed)); - hb->add_child(open_config_button); + add_child(open_config_button); - input_event_text = memnew(Label); - hb->add_child(input_event_text); + add_child(memnew(Control)); config_dialog = memnew(InputEventConfigurationDialog); config_dialog->connect("confirmed", callable_mp(this, &InputEventConfigContainer::_config_dialog_confirmed)); add_child(config_dialog); } +/////////////////////// + bool EditorInspectorPluginInputEvent::can_handle(Object *p_object) { Ref<InputEventKey> k = Ref<InputEventKey>(p_object); Ref<InputEventMouseButton> m = Ref<InputEventMouseButton>(p_object); @@ -115,6 +115,8 @@ void EditorInspectorPluginInputEvent::parse_begin(Object *p_object) { add_custom_control(picker_controls); } +/////////////////////// + InputEventEditorPlugin::InputEventEditorPlugin() { Ref<EditorInspectorPluginInputEvent> plugin; plugin.instantiate(); diff --git a/editor/plugins/input_event_editor_plugin.h b/editor/plugins/input_event_editor_plugin.h index 3c658a86e9..344f176e78 100644 --- a/editor/plugins/input_event_editor_plugin.h +++ b/editor/plugins/input_event_editor_plugin.h @@ -35,8 +35,8 @@ #include "editor/editor_inspector.h" #include "editor/editor_plugin.h" -class InputEventConfigContainer : public HBoxContainer { - GDCLASS(InputEventConfigContainer, HBoxContainer); +class InputEventConfigContainer : public VBoxContainer { + GDCLASS(InputEventConfigContainer, VBoxContainer); Label *input_event_text = nullptr; Button *open_config_button = nullptr; @@ -50,10 +50,10 @@ class InputEventConfigContainer : public HBoxContainer { void _event_changed(); protected: + void _notification(int p_what); static void _bind_methods(); public: - virtual Size2 get_minimum_size() const override; void set_event(const Ref<InputEvent> &p_event); InputEventConfigContainer(); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 855fc2b2a9..f2ca1fcfeb 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -7120,7 +7120,9 @@ void Node3DEditor::_request_gizmo(Object *p_obj) { } } } - sp->update_gizmos(); + if (!sp->get_gizmos().is_empty()) { + sp->update_gizmos(); + } } } diff --git a/editor/plugins/ot_features_plugin.cpp b/editor/plugins/ot_features_plugin.cpp index 27b35d803c..936eb747b0 100644 --- a/editor/plugins/ot_features_plugin.cpp +++ b/editor/plugins/ot_features_plugin.cpp @@ -145,8 +145,11 @@ void OpenTypeFeaturesAdd::setup(Object *p_object) { void OpenTypeFeaturesAdd::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_ENTER_TREE: { + connect("pressed", callable_mp(this, &OpenTypeFeaturesAdd::_features_menu)); + [[fallthrough]]; + } + case NOTIFICATION_THEME_CHANGED: { set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); } break; } @@ -173,7 +176,6 @@ OpenTypeFeaturesAdd::OpenTypeFeaturesAdd() { menu_cu->set_name("CUMenu"); menu->add_child(menu_cu); - connect("pressed", callable_mp(this, &OpenTypeFeaturesAdd::_features_menu)); menu->connect("id_pressed", callable_mp(this, &OpenTypeFeaturesAdd::_add_feature)); menu_cv->connect("id_pressed", callable_mp(this, &OpenTypeFeaturesAdd::_add_feature)); menu_ss->connect("id_pressed", callable_mp(this, &OpenTypeFeaturesAdd::_add_feature)); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 0f45415c4d..906edb006c 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1091,13 +1091,13 @@ void ScriptEditor::_file_dialog_action(String p_file) { switch (file_dialog_option) { case FILE_NEW_TEXTFILE: { Error err; - FileAccess *file = FileAccess::open(p_file, FileAccess::WRITE, &err); - if (err) { - EditorNode::get_singleton()->show_warning(TTR("Error writing TextFile:") + "\n" + p_file, TTR("Error!")); - break; + { + Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err); + if (err) { + EditorNode::get_singleton()->show_warning(TTR("Error writing TextFile:") + "\n" + p_file, TTR("Error!")); + break; + } } - file->close(); - memdelete(file); if (EditorFileSystem::get_singleton()) { if (textfile_extensions.has(p_file.get_extension())) { @@ -2211,17 +2211,16 @@ Error ScriptEditor::_save_text_file(Ref<TextFile> p_text_file, const String &p_p String source = sqscr->get_text(); Error err; - FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + { + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); - ERR_FAIL_COND_V_MSG(err, err, "Cannot save text file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err, err, "Cannot save text file '" + p_path + "'."); - file->store_string(source); - if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { - memdelete(file); - return ERR_CANT_CREATE; + file->store_string(source); + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + return ERR_CANT_CREATE; + } } - file->close(); - memdelete(file); if (ResourceSaver::get_timestamp_on_save()) { p_text_file->set_last_modified_time(FileAccess::get_modified_time(p_path)); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 87b5b829e0..27160f8c86 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -43,6 +43,11 @@ #include "scene/gui/margin_container.h" #include "scene/gui/panel_container.h" +static void _draw_shadowed_line(Control *p_control, const Point2 &p_from, const Size2 &p_size, const Size2 &p_shadow_offset, Color p_color, Color p_shadow_color) { + p_control->draw_line(p_from, p_from + p_size, p_color); + p_control->draw_line(p_from + p_shadow_offset, p_from + p_size + p_shadow_offset, p_shadow_color); +} + void SpriteFramesEditor::gui_input(const Ref<InputEvent> &p_event) { } @@ -58,46 +63,62 @@ void SpriteFramesEditor::_open_sprite_sheet() { } int SpriteFramesEditor::_sheet_preview_position_to_frame_index(const Point2 &p_position) { - if (p_position.x < 0 || p_position.y < 0) { - return -1; - } + const Size2i offset = _get_offset(); + const Size2i frame_size = _get_frame_size(); + const Size2i separation = _get_separation(); + const Size2i block_size = frame_size + separation; + const Point2i position = p_position / sheet_zoom - offset; - Size2i texture_size = split_sheet_preview->get_texture()->get_size(); - int h = split_sheet_h->get_value(); - int v = split_sheet_v->get_value(); - if (h > texture_size.width || v > texture_size.height) { - return -1; + if (position.x % block_size.x > frame_size.x || position.y % block_size.y > frame_size.y) { + return -1; // Gap between frames. } - int x = int(p_position.x / sheet_zoom) / (texture_size.width / h); - int y = int(p_position.y / sheet_zoom) / (texture_size.height / v); - if (x >= h || y >= v) { - return -1; + const Point2i frame = position / block_size; + const Size2i frame_count = _get_frame_count(); + if (frame.x < 0 || frame.y < 0 || frame.x >= frame_count.x || frame.y >= frame_count.y) { + return -1; // Out of bound. } - return h * y + x; + + return frame_count.x * frame.y + frame.x; } void SpriteFramesEditor::_sheet_preview_draw() { - Size2i texture_size = split_sheet_preview->get_texture()->get_size(); - int h = split_sheet_h->get_value(); - int v = split_sheet_v->get_value(); - - real_t width = (texture_size.width / h) * sheet_zoom; - real_t height = (texture_size.height / v) * sheet_zoom; - const float a = 0.3; - - real_t y_end = v * height; - for (int i = 0; i <= h; i++) { - real_t x = i * width; - split_sheet_preview->draw_line(Point2(x, 0), Point2(x, y_end), Color(1, 1, 1, a)); - split_sheet_preview->draw_line(Point2(x + 1, 0), Point2(x + 1, y_end), Color(0, 0, 0, a)); + const Size2i frame_count = _get_frame_count(); + const Size2i separation = _get_separation(); + + const Size2 draw_offset = Size2(_get_offset()) * sheet_zoom; + const Size2 draw_sep = Size2(separation) * sheet_zoom; + const Size2 draw_frame_size = Size2(_get_frame_size()) * sheet_zoom; + const Size2 draw_size = draw_frame_size * frame_count + draw_sep * (frame_count - Size2i(1, 1)); + + const Color line_color = Color(1, 1, 1, 0.3); + const Color shadow_color = Color(0, 0, 0, 0.3); + + // Vertical lines. + _draw_shadowed_line(split_sheet_preview, draw_offset, Vector2(0, draw_size.y), Vector2(1, 0), line_color, shadow_color); + for (int i = 0; i < frame_count.x - 1; i++) { + const Point2 start = draw_offset + Vector2(i * draw_sep.x + (i + 1) * draw_frame_size.x, 0); + if (separation.x == 0) { + _draw_shadowed_line(split_sheet_preview, start, Vector2(0, draw_size.y), Vector2(1, 0), line_color, shadow_color); + } else { + const Size2 size = Size2(draw_sep.x, draw_size.y); + split_sheet_preview->draw_rect(Rect2(start, size), line_color); + } } - real_t x_end = h * width; - for (int i = 0; i <= v; i++) { - real_t y = i * height; - split_sheet_preview->draw_line(Point2(0, y), Point2(x_end, y), Color(1, 1, 1, a)); - split_sheet_preview->draw_line(Point2(0, y + 1), Point2(x_end, y + 1), Color(0, 0, 0, a)); + _draw_shadowed_line(split_sheet_preview, draw_offset + Vector2(draw_size.x, 0), Vector2(0, draw_size.y), Vector2(1, 0), line_color, shadow_color); + + // Horizontal lines. + _draw_shadowed_line(split_sheet_preview, draw_offset, Vector2(draw_size.x, 0), Vector2(0, 1), line_color, shadow_color); + for (int i = 0; i < frame_count.y - 1; i++) { + const Point2 start = draw_offset + Vector2(0, i * draw_sep.y + (i + 1) * draw_frame_size.y); + if (separation.y == 0) { + _draw_shadowed_line(split_sheet_preview, start, Vector2(draw_size.x, 0), Vector2(0, 1), line_color, shadow_color); + } else { + const Size2 size = Size2(draw_size.x, draw_sep.y); + split_sheet_preview->draw_rect(Rect2(start, size), line_color); + } } + _draw_shadowed_line(split_sheet_preview, draw_offset + Vector2(0, draw_size.y), Vector2(draw_size.x, 0), Vector2(0, 1), line_color, shadow_color); if (frames_selected.size() == 0) { split_sheet_dialog->get_ok_button()->set_disabled(true); @@ -105,22 +126,20 @@ void SpriteFramesEditor::_sheet_preview_draw() { return; } - Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor")); + Color accent = get_theme_color("accent_color", "Editor"); for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { - int idx = E->get(); - int xp = idx % h; - int yp = idx / h; - real_t x = xp * width; - real_t y = yp * height; - - split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 0.35), true); - split_sheet_preview->draw_rect(Rect2(x + 0, y + 0, width - 0, height - 0), Color(0, 0, 0, 1), false); - split_sheet_preview->draw_rect(Rect2(x + 1, y + 1, width - 2, height - 2), Color(0, 0, 0, 1), false); - split_sheet_preview->draw_rect(Rect2(x + 2, y + 2, width - 4, height - 4), accent, false); - split_sheet_preview->draw_rect(Rect2(x + 3, y + 3, width - 6, height - 6), accent, false); - split_sheet_preview->draw_rect(Rect2(x + 4, y + 4, width - 8, height - 8), Color(0, 0, 0, 1), false); - split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 1), false); + const int idx = E->get(); + const int x = idx % frame_count.x; + const int y = idx / frame_count.x; + const Point2 pos = draw_offset + Point2(x, y) * (draw_frame_size + draw_sep); + split_sheet_preview->draw_rect(Rect2(pos + Size2(5, 5), draw_frame_size - Size2(10, 10)), Color(0, 0, 0, 0.35), true); + split_sheet_preview->draw_rect(Rect2(pos, draw_frame_size), Color(0, 0, 0, 1), false); + split_sheet_preview->draw_rect(Rect2(pos + Size2(1, 1), draw_frame_size - Size2(2, 2)), Color(0, 0, 0, 1), false); + split_sheet_preview->draw_rect(Rect2(pos + Size2(2, 2), draw_frame_size - Size2(4, 4)), accent, false); + split_sheet_preview->draw_rect(Rect2(pos + Size2(3, 3), draw_frame_size - Size2(6, 6)), accent, false); + split_sheet_preview->draw_rect(Rect2(pos + Size2(4, 4), draw_frame_size - Size2(8, 8)), Color(0, 0, 0, 1), false); + split_sheet_preview->draw_rect(Rect2(pos + Size2(5, 5), draw_frame_size - Size2(10, 10)), Color(0, 0, 0, 1), false); } split_sheet_dialog->get_ok_button()->set_disabled(false); @@ -223,10 +242,10 @@ void SpriteFramesEditor::_sheet_scroll_input(const Ref<InputEvent> &p_event) { } void SpriteFramesEditor::_sheet_add_frames() { - Size2i texture_size = split_sheet_preview->get_texture()->get_size(); - int frame_count_x = split_sheet_h->get_value(); - int frame_count_y = split_sheet_v->get_value(); - Size2 frame_size(texture_size.width / frame_count_x, texture_size.height / frame_count_y); + const Size2i frame_count = _get_frame_count(); + const Size2i frame_size = _get_frame_size(); + const Size2i offset = _get_offset(); + const Size2i separation = _get_separation(); undo_redo->create_action(TTR("Add Frame")); @@ -234,12 +253,12 @@ void SpriteFramesEditor::_sheet_add_frames() { for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { int idx = E->get(); - Point2 frame_coords(idx % frame_count_x, idx / frame_count_x); + const Point2 frame_coords(idx % frame_count.x, idx / frame_count.x); Ref<AtlasTexture> at; at.instantiate(); at->set_atlas(split_sheet_preview->get_texture()); - at->set_region(Rect2(frame_coords * frame_size, frame_size)); + at->set_region(Rect2(offset + frame_coords * (frame_size + separation), frame_size)); undo_redo->add_do_method(frames, "add_frame", edited_anim, at, -1); undo_redo->add_undo_method(frames, "remove_frame", edited_anim, fc); @@ -293,7 +312,57 @@ void SpriteFramesEditor::_sheet_select_clear_all_frames() { split_sheet_preview->update(); } -void SpriteFramesEditor::_sheet_spin_changed(double) { +void SpriteFramesEditor::_sheet_spin_changed(double p_value, int p_dominant_param) { + if (updating_split_settings) { + return; + } + updating_split_settings = true; + + if (p_dominant_param != PARAM_USE_CURRENT) { + dominant_param = p_dominant_param; + } + + const Size2i texture_size = split_sheet_preview->get_texture()->get_size(); + const Size2i size = texture_size - _get_offset(); + + switch (dominant_param) { + case PARAM_SIZE: { + const Size2i frame_size = _get_frame_size(); + + const Size2i offset_max = texture_size - frame_size; + split_sheet_offset_x->set_max(offset_max.x); + split_sheet_offset_y->set_max(offset_max.y); + + const Size2i sep_max = size - frame_size * 2; + split_sheet_sep_x->set_max(sep_max.x); + split_sheet_sep_y->set_max(sep_max.y); + + const Size2i separation = _get_separation(); + const Size2i count = (size + separation) / (frame_size + separation); + split_sheet_h->set_value(count.x); + split_sheet_v->set_value(count.y); + } break; + + case PARAM_FRAME_COUNT: { + const Size2i count = _get_frame_count(); + + const Size2i offset_max = texture_size - count; + split_sheet_offset_x->set_max(offset_max.x); + split_sheet_offset_y->set_max(offset_max.y); + + const Size2i gap_count = count - Size2i(1, 1); + split_sheet_sep_x->set_max(gap_count.x == 0 ? size.x : (size.x - count.x) / gap_count.x); + split_sheet_sep_y->set_max(gap_count.y == 0 ? size.y : (size.y - count.y) / gap_count.y); + + const Size2i separation = _get_separation(); + const Size2i frame_size = (size - separation * gap_count) / count; + split_sheet_size_x->set_value(frame_size.x); + split_sheet_size_y->set_value(frame_size.y); + } break; + } + + updating_split_settings = false; + frames_selected.clear(); last_frame_selected = -1; split_sheet_preview->update(); @@ -311,10 +380,29 @@ void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) { bool new_texture = texture != split_sheet_preview->get_texture(); split_sheet_preview->set_texture(texture); if (new_texture) { - //different texture, reset to 4x4 + // Reset spin max. + const Size2i size = texture->get_size(); + split_sheet_size_x->set_max(size.x); + split_sheet_size_y->set_max(size.y); + split_sheet_sep_x->set_max(size.x); + split_sheet_sep_y->set_max(size.y); + split_sheet_offset_x->set_max(size.x); + split_sheet_offset_y->set_max(size.y); + + // Different texture, reset to 4x4. + dominant_param = PARAM_FRAME_COUNT; + updating_split_settings = true; split_sheet_h->set_value(4); split_sheet_v->set_value(4); - //reset zoom + split_sheet_size_x->set_value(size.x / 4); + split_sheet_size_y->set_value(size.y / 4); + split_sheet_sep_x->set_value(0); + split_sheet_sep_y->set_value(0); + split_sheet_offset_x->set_value(0); + split_sheet_offset_y->set_value(0); + updating_split_settings = false; + + // Reset zoom. _sheet_zoom_reset(); } split_sheet_dialog->popup_centered_ratio(0.65); @@ -392,6 +480,22 @@ void SpriteFramesEditor::_file_load_request(const Vector<String> &p_path, int p_ undo_redo->commit_action(); } +Size2i SpriteFramesEditor::_get_frame_count() const { + return Size2i(split_sheet_h->get_value(), split_sheet_v->get_value()); +} + +Size2i SpriteFramesEditor::_get_frame_size() const { + return Size2i(split_sheet_size_x->get_value(), split_sheet_size_y->get_value()); +} + +Size2i SpriteFramesEditor::_get_offset() const { + return Size2i(split_sheet_offset_x->get_value(), split_sheet_offset_y->get_value()); +} + +Size2i SpriteFramesEditor::_get_separation() const { + return Size2i(split_sheet_sep_x->get_value(), split_sheet_sep_y->get_value()); +} + void SpriteFramesEditor::_load_pressed() { ERR_FAIL_COND(!frames->has_animation(edited_anim)); loading_scene = false; @@ -1210,23 +1314,66 @@ SpriteFramesEditor::SpriteFramesEditor() { HBoxContainer *split_sheet_hb = memnew(HBoxContainer); - Label *ss_label = memnew(Label(TTR("Horizontal:"))); - split_sheet_hb->add_child(ss_label); + split_sheet_hb->add_child(memnew(Label(TTR("Horizontal:")))); split_sheet_h = memnew(SpinBox); split_sheet_h->set_min(1); split_sheet_h->set_max(128); split_sheet_h->set_step(1); split_sheet_hb->add_child(split_sheet_h); - split_sheet_h->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed)); + split_sheet_h->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed), varray(PARAM_FRAME_COUNT)); - ss_label = memnew(Label(TTR("Vertical:"))); - split_sheet_hb->add_child(ss_label); + split_sheet_hb->add_child(memnew(Label(TTR("Vertical:")))); split_sheet_v = memnew(SpinBox); split_sheet_v->set_min(1); split_sheet_v->set_max(128); split_sheet_v->set_step(1); split_sheet_hb->add_child(split_sheet_v); - split_sheet_v->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed)); + split_sheet_v->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed), varray(PARAM_FRAME_COUNT)); + + split_sheet_hb->add_child(memnew(VSeparator)); + split_sheet_hb->add_child(memnew(Label(TTR("Size:")))); + split_sheet_size_x = memnew(SpinBox); + split_sheet_size_x->set_min(1); + split_sheet_size_x->set_step(1); + split_sheet_size_x->set_suffix("px"); + split_sheet_size_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed), varray(PARAM_SIZE)); + split_sheet_hb->add_child(split_sheet_size_x); + split_sheet_size_y = memnew(SpinBox); + split_sheet_size_y->set_min(1); + split_sheet_size_y->set_step(1); + split_sheet_size_y->set_suffix("px"); + split_sheet_size_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed), varray(PARAM_SIZE)); + split_sheet_hb->add_child(split_sheet_size_y); + + split_sheet_hb->add_child(memnew(VSeparator)); + split_sheet_hb->add_child(memnew(Label(TTR("Separation:")))); + split_sheet_sep_x = memnew(SpinBox); + split_sheet_sep_x->set_min(0); + split_sheet_sep_x->set_step(1); + split_sheet_sep_x->set_suffix("px"); + split_sheet_sep_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed), varray(PARAM_USE_CURRENT)); + split_sheet_hb->add_child(split_sheet_sep_x); + split_sheet_sep_y = memnew(SpinBox); + split_sheet_sep_y->set_min(0); + split_sheet_sep_y->set_step(1); + split_sheet_sep_y->set_suffix("px"); + split_sheet_sep_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed), varray(PARAM_USE_CURRENT)); + split_sheet_hb->add_child(split_sheet_sep_y); + + split_sheet_hb->add_child(memnew(VSeparator)); + split_sheet_hb->add_child(memnew(Label(TTR("Offset:")))); + split_sheet_offset_x = memnew(SpinBox); + split_sheet_offset_x->set_min(0); + split_sheet_offset_x->set_step(1); + split_sheet_offset_x->set_suffix("px"); + split_sheet_offset_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed), varray(PARAM_USE_CURRENT)); + split_sheet_hb->add_child(split_sheet_offset_x); + split_sheet_offset_y = memnew(SpinBox); + split_sheet_offset_y->set_min(0); + split_sheet_offset_y->set_step(1); + split_sheet_offset_y->set_suffix("px"); + split_sheet_offset_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed), varray(PARAM_USE_CURRENT)); + split_sheet_hb->add_child(split_sheet_offset_y); split_sheet_hb->add_spacer(); diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index b0213012a2..9a00fe5771 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -48,6 +48,13 @@ class EditorFileDialog; class SpriteFramesEditor : public HSplitContainer { GDCLASS(SpriteFramesEditor, HSplitContainer); + enum { + PARAM_USE_CURRENT, // Used in callbacks to indicate `dominant_param` should be not updated. + PARAM_FRAME_COUNT, // Keep "Horizontal" & "Vertial" values. + PARAM_SIZE, // Keep "Size" values. + }; + int dominant_param = PARAM_FRAME_COUNT; + Button *load = nullptr; Button *load_sheet = nullptr; Button *_delete = nullptr; @@ -86,6 +93,12 @@ class SpriteFramesEditor : public HSplitContainer { TextureRect *split_sheet_preview = nullptr; SpinBox *split_sheet_h = nullptr; SpinBox *split_sheet_v = nullptr; + SpinBox *split_sheet_size_x = nullptr; + SpinBox *split_sheet_size_y = nullptr; + SpinBox *split_sheet_sep_x = nullptr; + SpinBox *split_sheet_sep_y = nullptr; + SpinBox *split_sheet_offset_x = nullptr; + SpinBox *split_sheet_offset_y = nullptr; Button *split_sheet_zoom_out = nullptr; Button *split_sheet_zoom_reset = nullptr; Button *split_sheet_zoom_in = nullptr; @@ -103,6 +116,11 @@ class SpriteFramesEditor : public HSplitContainer { float max_sheet_zoom; float min_sheet_zoom; + Size2i _get_frame_count() const; + Size2i _get_frame_size() const; + Size2i _get_offset() const; + Size2i _get_separation() const; + void _load_pressed(); void _file_load_request(const Vector<String> &p_path, int p_at_pos = -1); void _copy_pressed(); @@ -128,6 +146,7 @@ class SpriteFramesEditor : public HSplitContainer { void _zoom_reset(); bool updating; + bool updating_split_settings = false; // Skip SpinBox/Range callback when setting value by code. UndoRedo *undo_redo = nullptr; @@ -139,7 +158,7 @@ class SpriteFramesEditor : public HSplitContainer { void _prepare_sprite_sheet(const String &p_file); int _sheet_preview_position_to_frame_index(const Vector2 &p_position); void _sheet_preview_draw(); - void _sheet_spin_changed(double); + void _sheet_spin_changed(double p_value, int p_dominant_param); void _sheet_preview_input(const Ref<InputEvent> &p_event); void _sheet_scroll_input(const Ref<InputEvent> &p_event); void _sheet_add_frames(); diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index a7c06ada5c..15f03fd46d 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -70,7 +70,7 @@ void TexturePreview::_update_metadata_label_text() { format = texture->get_class(); } - metadata_label->set_text(itos(texture->get_width()) + "x" + itos(texture->get_height()) + " " + format); + metadata_label->set_text(vformat(String::utf8("%s×%s %s"), itos(texture->get_width()), itos(texture->get_height()), format)); } TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) { diff --git a/editor/pot_generator.cpp b/editor/pot_generator.cpp index 5afd460831..0835d0212f 100644 --- a/editor/pot_generator.cpp +++ b/editor/pot_generator.cpp @@ -93,7 +93,7 @@ void POTGenerator::generate_pot(const String &p_file) { void POTGenerator::_write_to_pot(const String &p_file) { Error err; - FileAccess *file = FileAccess::open(p_file, FileAccess::WRITE, &err); + Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err); if (err != OK) { ERR_PRINT("Failed to open " + p_file); return; @@ -155,11 +155,9 @@ void POTGenerator::_write_to_pot(const String &p_file) { } } } - - file->close(); } -void POTGenerator::_write_msgid(FileAccess *r_file, const String &p_id, bool p_plural) { +void POTGenerator::_write_msgid(Ref<FileAccess> r_file, const String &p_id, bool p_plural) { // Split \\n and \n. Vector<String> temp = p_id.split("\\n"); Vector<String> msg_lines; diff --git a/editor/pot_generator.h b/editor/pot_generator.h index 2b42c681e5..e7a5f90cee 100644 --- a/editor/pot_generator.h +++ b/editor/pot_generator.h @@ -49,7 +49,7 @@ class POTGenerator { OrderedHashMap<String, Vector<MsgidData>> all_translation_strings; void _write_to_pot(const String &p_file); - void _write_msgid(FileAccess *r_file, const String &p_id, bool p_plural); + void _write_msgid(Ref<FileAccess> r_file, const String &p_id, bool p_plural); void _add_new_msgid(const String &p_msgid, const String &p_context, const String &p_plural, const String &p_location); #ifdef DEBUG_POT diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 501cb88547..4ca0f18f0e 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -147,7 +147,7 @@ private: } String _test_path() { - DirAccessRef d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String valid_path, valid_install_path; if (d->change_dir(project_path->get_text()) == OK) { valid_path = project_path->get_text(); @@ -186,8 +186,7 @@ private: if (mode == MODE_IMPORT || mode == MODE_RENAME) { if (!valid_path.is_empty() && !d->file_exists("project.godot")) { if (valid_path.ends_with(".zip")) { - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + zlib_filefunc_def io = zipio_create_io(); unzFile pkg = unzOpen2(valid_path.utf8().get_data(), &io); if (!pkg) { @@ -383,7 +382,7 @@ private: return; } - DirAccessRef d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); if (d->change_dir(project_path->get_text()) == OK) { if (!d->dir_exists(project_name_no_edges)) { if (d->make_dir(project_name_no_edges) == OK) { @@ -500,8 +499,7 @@ private: zip_path = project_path->get_text(); } - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + zlib_filefunc_def io = zipio_create_io(); unzFile pkg = unzOpen2(zip_path.utf8().get_data(), &io); if (!pkg) { @@ -549,7 +547,7 @@ private: path = path.substr(0, path.length() - 1); String rel_path = path.substr(zip_root.length()); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); da->make_dir(dir.plus_file(rel_path)); } else { Vector<uint8_t> data; @@ -559,16 +557,12 @@ private: //read unzOpenCurrentFile(pkg); ret = unzReadCurrentFile(pkg, data.ptrw(), data.size()); - if (ret != UNZ_OK) { - break; - } + ERR_BREAK_MSG(ret < 0, vformat("An error occurred while attempting to read from file: %s. This file will not be used.", rel_path)); unzCloseCurrentFile(pkg); - FileAccess *f = FileAccess::open(dir.plus_file(rel_path), FileAccess::WRITE); - - if (f) { + Ref<FileAccess> f = FileAccess::open(dir.plus_file(rel_path), FileAccess::WRITE); + if (f.is_valid()) { f->store_buffer(data.ptr(), data.size()); - memdelete(f); } else { failed_files.push_back(rel_path); } @@ -615,7 +609,7 @@ private: void _remove_created_folder() { if (!created_folder_path.is_empty()) { - DirAccessRef d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); d->remove(created_folder_path); create_dir->set_disabled(false); @@ -719,7 +713,7 @@ public: project_path->set_text(fav_dir); fdialog->set_current_dir(fav_dir); } else { - DirAccessRef d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); project_path->set_text(d->get_current_dir()); fdialog->set_current_dir(d->get_current_dir()); } @@ -1125,7 +1119,7 @@ struct ProjectListComparator { }; ProjectList::ProjectList() { - _order_option = FilterOption::NAME; + _order_option = FilterOption::EDIT_DATE; _scroll_children = memnew(VBoxContainer); _scroll_children->set_h_size_flags(Control::SIZE_EXPAND_FILL); add_child(_scroll_children); @@ -2280,7 +2274,7 @@ void ProjectManager::_run_project() { } void ProjectManager::_scan_dir(const String &path, List<String> *r_projects) { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); Error error = da->change_dir(path); ERR_FAIL_COND_MSG(error != OK, "Could not scan directory at: " + path); da->list_dir_begin(); @@ -2402,14 +2396,14 @@ void ProjectManager::_install_project(const String &p_zip_path, const String &p_ npdialog->show_dialog(); } -void ProjectManager::_files_dropped(PackedStringArray p_files, int p_screen) { +void ProjectManager::_files_dropped(PackedStringArray p_files) { if (p_files.size() == 1 && p_files[0].ends_with(".zip")) { const String file = p_files[0].get_file(); _install_project(p_files[0], file.substr(0, file.length() - 4).capitalize()); return; } Set<String> folders_set; - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); for (int i = 0; i < p_files.size(); i++) { String file = p_files[i]; folders_set.insert(da->dir_exists(file) ? file : file.get_base_dir()); @@ -2422,7 +2416,7 @@ void ProjectManager::_files_dropped(PackedStringArray p_files, int p_screen) { bool confirm = true; if (folders.size() == 1) { - DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); if (dir->change_dir(folders[0]) == OK) { dir->list_dir_begin(); String file = dir->get_next(); @@ -2610,9 +2604,9 @@ ProjectManager::ProjectManager() { hb->add_child(filter_option); Vector<String> sort_filter_titles; + sort_filter_titles.push_back(TTR("Last Edited")); sort_filter_titles.push_back(TTR("Name")); sort_filter_titles.push_back(TTR("Path")); - sort_filter_titles.push_back(TTR("Last Edited")); for (int i = 0; i < sort_filter_titles.size(); i++) { filter_option->add_item(sort_filter_titles[i]); @@ -2849,7 +2843,7 @@ ProjectManager::ProjectManager() { _load_recent_projects(); - DirAccessRef dir_access = DirAccess::create(DirAccess::AccessType::ACCESS_FILESYSTEM); + Ref<DirAccess> dir_access = DirAccess::create(DirAccess::AccessType::ACCESS_FILESYSTEM); String default_project_path = EditorSettings::get_singleton()->get("filesystem/directories/default_project_path"); if (!dir_access->dir_exists(default_project_path)) { diff --git a/editor/project_manager.h b/editor/project_manager.h index 9cea6e163f..a68cc4350c 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -42,9 +42,9 @@ class ProjectDialog; class ProjectList; enum FilterOption { + EDIT_DATE, NAME, PATH, - EDIT_DATE, }; class ProjectManager : public Control { @@ -127,7 +127,7 @@ class ProjectManager : public Control { void _dim_window(); virtual void shortcut_input(const Ref<InputEvent> &p_ev) override; - void _files_dropped(PackedStringArray p_files, int p_screen); + void _files_dropped(PackedStringArray p_files); void _version_button_pressed(); void _on_order_option_changed(int p_idx); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 71ea625013..03f65cdf52 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -3149,9 +3149,8 @@ void SceneTreeDock::_update_create_root_dialog() { favorite_nodes->get_child(i)->queue_delete(); } - FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites.Node"), FileAccess::READ); - - if (f) { + Ref<FileAccess> f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites.Node"), FileAccess::READ); + if (f.is_valid()) { while (!f->eof_reached()) { String l = f->get_line().strip_edges(); @@ -3168,8 +3167,6 @@ void SceneTreeDock::_update_create_root_dialog() { button->connect("pressed", callable_mp(this, &SceneTreeDock::_favorite_root_selected), make_binds(l)); } } - - memdelete(f); } if (!favorite_nodes->is_visible_in_tree()) { diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 72f77c859b..7d063e13f9 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -246,7 +246,7 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must } { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (da->change_dir(p.get_base_dir()) != OK) { return TTR("Base path is invalid."); } @@ -254,7 +254,7 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must { // Check if file exists. - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (da->dir_exists(p)) { return TTR("A directory with the same name exists."); } else if (p_file_must_exist && !da->file_exists(p)) { @@ -547,7 +547,7 @@ void ScriptCreateDialog::_path_changed(const String &p_path) { } // Check if file exists. - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges()); if (da->file_exists(p)) { is_new_script_created = false; @@ -824,8 +824,8 @@ Vector<ScriptLanguage::ScriptTemplate> ScriptCreateDialog::_get_user_templates(c String dir_path = p_dir.plus_file(p_object); - DirAccessRef d = DirAccess::open(dir_path); - if (d) { + Ref<DirAccess> d = DirAccess::open(dir_path); + if (d.is_valid()) { d->list_dir_begin(); String file = d->get_next(); while (file != String()) { @@ -858,7 +858,7 @@ ScriptLanguage::ScriptTemplate ScriptCreateDialog::_parse_template(const ScriptL // Parse file for meta-information and script content Error err; - FileAccess *file = FileAccess::open(p_path.plus_file(p_filename), FileAccess::READ, &err); + Ref<FileAccess> file = FileAccess::open(p_path.plus_file(p_filename), FileAccess::READ, &err); if (!err) { while (!file->eof_reached()) { String line = file->get_line(); @@ -890,8 +890,6 @@ ScriptLanguage::ScriptTemplate ScriptCreateDialog::_parse_template(const ScriptL script_template.content += line.replace("\t", "_TS_") + "\n"; } } - file->close(); - memdelete(file); } script_template.content = script_template.content.lstrip("\n"); diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp index dbc78e846c..f07ec161c2 100644 --- a/editor/shader_create_dialog.cpp +++ b/editor/shader_create_dialog.cpp @@ -313,7 +313,7 @@ void ShaderCreateDialog::_path_changed(const String &p_path) { return; } - DirAccessRef f = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> f = DirAccess::create(DirAccess::ACCESS_RESOURCES); String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges()); if (f->file_exists(p)) { is_new_shader_created = false; @@ -371,12 +371,12 @@ String ShaderCreateDialog::_validate_path(const String &p_path) { return TTR("Path is not local."); } - DirAccessRef d = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (d->change_dir(p.get_base_dir()) != OK) { return TTR("Invalid base path."); } - DirAccessRef f = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> f = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (f->dir_exists(p)) { return TTR("A directory with the same name exists."); } |