diff options
Diffstat (limited to 'editor')
44 files changed, 759 insertions, 375 deletions
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index ab8ae71904..67b52bf0ca 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -762,7 +762,7 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) { undo_redo->create_action(TTR("Add Bezier Point")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, time, new_point); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, time); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, time); undo_redo->commit_action(); //then attempt to move @@ -857,7 +857,7 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) { continue; //already in selection, don't save } - undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", track, newtime); + undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track, newtime); AnimMoveRestore amr; amr.key = animation->track_get_key_value(track, idx); @@ -888,7 +888,7 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) { if (newpos<0) continue; //no remove what no inserted */ - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, newpos); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, newpos); } // 5-(undo) reinsert keys @@ -1038,7 +1038,7 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) { undo_redo->create_action(TTR("Add Bezier Point")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, time, new_point); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, time); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, time); undo_redo->commit_action(); } break; @@ -1074,7 +1074,7 @@ void AnimationBezierTrackEdit::duplicate_selection() { int existing_idx = animation->track_find_key(track, dst_time, true); undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, dst_time, animation->track_get_key_value(track, E->get()), animation->track_get_key_transition(track, E->get())); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, dst_time); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, dst_time); Pair<int, float> p; p.first = track; diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 9db2f0a287..89f77f3c77 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -134,7 +134,7 @@ public: undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, key); undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, val, trans); undo_redo->add_do_method(this, "_key_ofs_changed", animation, key_ofs, new_time); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, new_time); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, new_time); undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_ofs, val, trans); undo_redo->add_undo_method(this, "_key_ofs_changed", animation, new_time, key_ofs); @@ -758,7 +758,7 @@ public: undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, key); undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, val, trans); undo_redo->add_do_method(this, "_key_ofs_changed", animation, key_ofs, new_time); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, new_time); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, new_time); undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_ofs, val, trans); undo_redo->add_undo_method(this, "_key_ofs_changed", animation, new_time, key_ofs); @@ -3979,7 +3979,7 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count()); p_next_tracks.normal++; } else { - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_id.track_idx, time); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_id.track_idx, time); int existing = animation->track_find_key(p_id.track_idx, time, true); if (existing != -1) { Variant v = animation->track_get_key_value(p_id.track_idx, existing); @@ -4568,7 +4568,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { undo_redo->create_action(TTR("Add Transform Track Key")); undo_redo->add_do_method(animation.ptr(), "transform_track_insert_key", p_track, p_ofs, loc, rot, scale); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); undo_redo->commit_action(); } break; @@ -4580,7 +4580,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { undo_redo->create_action(TTR("Add Track Key")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, value); undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); undo_redo->commit_action(); } break; @@ -4611,7 +4611,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { undo_redo->create_action(TTR("Add Track Key")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, arr); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); undo_redo->commit_action(); } break; @@ -4623,7 +4623,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { undo_redo->create_action(TTR("Add Track Key")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, ak); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); undo_redo->commit_action(); } break; case Animation::TYPE_ANIMATION: { @@ -4631,7 +4631,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { undo_redo->create_action(TTR("Add Track Key")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, anim); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); undo_redo->commit_action(); } break; } @@ -4669,7 +4669,7 @@ void AnimationTrackEditor::_add_method_key(const String &p_method) { undo_redo->create_action(TTR("Add Method Track Key")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", insert_key_from_track_call_track, insert_key_from_track_call_ofs, d); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", insert_key_from_track_call_track, insert_key_from_track_call_ofs); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", insert_key_from_track_call_track, insert_key_from_track_call_ofs); undo_redo->commit_action(); return; @@ -4879,7 +4879,7 @@ void AnimationTrackEditor::_move_selection_commit() { continue; //already in selection, don't save } - undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newtime); + undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newtime); _AnimMoveRestore amr; amr.key = animation->track_get_key_value(E->key().track, idx); @@ -4899,7 +4899,7 @@ void AnimationTrackEditor::_move_selection_commit() { // 4 - (undo) remove inserted keys for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) { float newpos = snap_time(E->get().pos + motion); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newpos); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newpos); } // 5 - (undo) reinsert keys @@ -5096,7 +5096,7 @@ void AnimationTrackEditor::_anim_duplicate_keys(bool transpose) { int existing_idx = animation->track_find_key(dst_track, dst_time, true); undo_redo->add_do_method(animation.ptr(), "track_insert_key", dst_track, dst_time, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key)); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", dst_track, dst_time); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", dst_track, dst_time); Pair<int, float> p; p.first = dst_track; @@ -5344,7 +5344,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { continue; //already in selection, don't save } - undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newtime); + undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newtime); _AnimMoveRestore amr; amr.key = animation->track_get_key_value(E->key().track, idx); @@ -5365,7 +5365,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { // 4-(undo) remove inserted keys for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) { float newpos = _NEW_POS(E->get().pos); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newpos); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newpos); } // 5-(undo) reinsert keys diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp index 506a327ffc..ae79299331 100644 --- a/editor/animation_track_editor_plugins.cpp +++ b/editor/animation_track_editor_plugins.cpp @@ -1024,7 +1024,7 @@ void AnimationTrackEditTypeAudio::drop_data(const Point2 &p_point, const Variant get_undo_redo()->create_action(TTR("Add Audio Track Clip")); get_undo_redo()->add_do_method(get_animation().ptr(), "audio_track_insert_key", get_track(), ofs, stream); - get_undo_redo()->add_undo_method(get_animation().ptr(), "track_remove_key_at_position", get_track(), ofs); + get_undo_redo()->add_undo_method(get_animation().ptr(), "track_remove_key_at_time", get_track(), ofs); get_undo_redo()->commit_action(); update(); diff --git a/editor/debugger/editor_debugger_server.cpp b/editor/debugger/editor_debugger_server.cpp index 4add891bcb..662f247062 100644 --- a/editor/debugger/editor_debugger_server.cpp +++ b/editor/debugger/editor_debugger_server.cpp @@ -40,7 +40,7 @@ class EditorDebuggerServerTCP : public EditorDebuggerServer { private: - Ref<TCP_Server> server; + Ref<TCPServer> server; public: static EditorDebuggerServer *create(const String &p_protocol); diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index d962658484..7527514dca 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -37,6 +37,9 @@ #include "core/version.h" #include "core/version_hash.gen.h" +// The metadata key used to store and retrieve the version text to copy to the clipboard. +static const String META_TEXT_TO_COPY = "text_to_copy"; + void EditorAbout::_theme_changed() { const Ref<Font> font = get_theme_font("source", "EditorFonts"); const int font_size = get_theme_font_size("source_size", "EditorFonts"); @@ -63,7 +66,12 @@ void EditorAbout::_license_tree_selected() { _tpl_text->set_text(selected->get_metadata(0)); } +void EditorAbout::_version_button_pressed() { + DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY)); +} + void EditorAbout::_bind_methods() { + ClassDB::bind_method(D_METHOD("_version_button_pressed"), &EditorAbout::_version_button_pressed); } TextureRect *EditorAbout::get_logo() const { @@ -124,17 +132,32 @@ EditorAbout::EditorAbout() { _logo = memnew(TextureRect); hbc->add_child(_logo); + VBoxContainer *version_info_vbc = memnew(VBoxContainer); + + // Add a dummy control node for spacing. + Control *v_spacer = memnew(Control); + version_info_vbc->add_child(v_spacer); + + version_btn = memnew(LinkButton); String hash = String(VERSION_HASH); if (hash.length() != 0) { hash = "." + hash.left(9); } + version_btn->set_text(VERSION_FULL_NAME + hash); + // Set the text to copy in metadata as it slightly differs from the button's text. + version_btn->set_meta(META_TEXT_TO_COPY, "v" VERSION_FULL_BUILD + hash); + version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); + version_btn->set_tooltip(TTR("Click to copy.")); + version_btn->connect("pressed", callable_mp(this, &EditorAbout::_version_button_pressed)); + version_info_vbc->add_child(version_btn); Label *about_text = memnew(Label); about_text->set_v_size_flags(Control::SIZE_SHRINK_CENTER); - about_text->set_text(VERSION_FULL_NAME + hash + - String::utf8("\n\xc2\xa9 2007-2021 Juan Linietsky, Ariel Manzur.\n\xc2\xa9 2014-2021 ") + + about_text->set_text(String::utf8("\xc2\xa9 2007-2021 Juan Linietsky, Ariel Manzur.\n\xc2\xa9 2014-2021 ") + TTR("Godot Engine contributors") + "\n"); - hbc->add_child(about_text); + version_info_vbc->add_child(about_text); + + hbc->add_child(version_info_vbc); TabContainer *tc = memnew(TabContainer); tc->set_custom_minimum_size(Size2(950, 400) * EDSCALE); diff --git a/editor/editor_about.h b/editor/editor_about.h index 2823220a8a..b76a2ada34 100644 --- a/editor/editor_about.h +++ b/editor/editor_about.h @@ -34,6 +34,7 @@ #include "scene/gui/control.h" #include "scene/gui/dialogs.h" #include "scene/gui/item_list.h" +#include "scene/gui/link_button.h" #include "scene/gui/rich_text_label.h" #include "scene/gui/scroll_container.h" #include "scene/gui/separator.h" @@ -53,8 +54,10 @@ class EditorAbout : public AcceptDialog { private: void _license_tree_selected(); + void _version_button_pressed(); ScrollContainer *_populate_list(const String &p_name, const List<String> &p_sections, const char *const *const p_src[], const int p_flag_single_column = 0); + LinkButton *version_btn; Tree *_tpl_tree; RichTextLabel *_license_text; RichTextLabel *_tpl_text; diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index fa4703d425..6405af3876 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -426,6 +426,18 @@ UndoRedo &EditorData::get_undo_redo() { return undo_redo; } +void EditorData::add_undo_redo_inspector_hook_callback(Callable p_callable) { + undo_redo_callbacks.push_back(p_callable); +} + +void EditorData::remove_undo_redo_inspector_hook_callback(Callable p_callable) { + undo_redo_callbacks.erase(p_callable); +} + +const Vector<Callable> EditorData::get_undo_redo_inspector_hook_callback() { + return undo_redo_callbacks; +} + void EditorData::remove_editor_plugin(EditorPlugin *p_plugin) { p_plugin->undo_redo = nullptr; editor_plugins.erase(p_plugin); diff --git a/editor/editor_data.h b/editor/editor_data.h index dbe729d9d9..2ece94d6a3 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -132,6 +132,7 @@ private: List<PropertyData> clipboard; UndoRedo undo_redo; + Vector<Callable> undo_redo_callbacks; void _cleanup_history(); @@ -166,6 +167,9 @@ public: EditorPlugin *get_editor_plugin(int p_idx); UndoRedo &get_undo_redo(); + void add_undo_redo_inspector_hook_callback(Callable p_callable); // Callbacks shoud have 4 args: (Object* undo_redo, Object *modified_object, String property, Varian new_value) + void remove_undo_redo_inspector_hook_callback(Callable p_callable); + const Vector<Callable> get_undo_redo_inspector_hook_callback(); void save_editor_global_states(); void restore_editor_global_states(); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 59d3b09678..495bdd42f7 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -68,6 +68,11 @@ int EditorFileSystemDirectory::find_dir_index(const String &p_dir) const { return -1; } +void EditorFileSystemDirectory::force_update() { + // We set modified_time to 0 to force `EditorFileSystem::_scan_fs_changes` to search changes in the directory + modified_time = 0; +} + int EditorFileSystemDirectory::get_subdir_count() const { return subdirs.size(); } @@ -854,9 +859,11 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const //then scan files and directories and check what's different - DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + + Error ret = da->change_dir(cd); + ERR_FAIL_COND_MSG(ret != OK, "Cannot change to '" + cd + "' folder."); - da->change_dir(cd); da->list_dir_begin(); while (true) { String f = da->get_next(); @@ -944,7 +951,6 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const } da->list_dir_end(); - memdelete(da); } for (int i = 0; i < p_dir->files.size(); i++) { diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 9c9076106c..855c320856 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -102,6 +102,8 @@ public: int find_file_index(const String &p_file) const; int find_dir_index(const String &p_dir) const; + void force_update(); + EditorFileSystemDirectory(); ~EditorFileSystemDirectory(); }; diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 738b2f9f82..5bb3c8b4d0 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -2266,6 +2266,22 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo undo_redo->add_do_property(object, p_name, p_value); undo_redo->add_undo_property(object, p_name, object->get(p_name)); + Variant v_undo_redo = (Object *)undo_redo; + Variant v_object = object; + Variant v_name = p_name; + for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_undo_redo_inspector_hook_callback().size(); i++) { + const Callable &callback = EditorNode::get_singleton()->get_editor_data().get_undo_redo_inspector_hook_callback()[i]; + + const Variant *p_arguments[] = { &v_undo_redo, &v_object, &v_name, &p_value }; + Variant return_value; + Callable::CallError call_error; + + callback.call(p_arguments, 4, return_value, call_error); + if (call_error.error != Callable::CallError::CALL_OK) { + ERR_PRINT("Invalid UndoRedo callback."); + } + } + if (p_refresh_all) { undo_redo->add_do_method(this, "_edit_request_change", object, ""); undo_redo->add_undo_method(this, "_edit_request_change", object, ""); diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 7b94016fb6..602035634a 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -63,6 +63,18 @@ void EditorLog::_notification(int p_what) { log->add_theme_font_override("normal_font", get_theme_font("output_source", "EditorFonts")); log->add_theme_font_size_override("normal_font_size", get_theme_font_size("output_source_size", "EditorFonts")); log->add_theme_color_override("selection_color", get_theme_color("accent_color", "Editor") * Color(1, 1, 1, 0.4)); + log->add_theme_font_override("bold_font", get_theme_font("bold", "EditorFonts")); + + type_filter_map[MSG_TYPE_STD]->toggle_button->set_icon(get_theme_icon("Popup", "EditorIcons")); + type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_icon(get_theme_icon("StatusError", "EditorIcons")); + type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_icon(get_theme_icon("StatusWarning", "EditorIcons")); + type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_icon(get_theme_icon("Edit", "EditorIcons")); + + clear_button->set_icon(get_theme_icon("Clear", "EditorIcons")); + copy_button->set_icon(get_theme_icon("ActionCopy", "EditorIcons")); + collapse_button->set_icon(get_theme_icon("CombineLines", "EditorIcons")); + show_search_button->set_icon(get_theme_icon("Search", "EditorIcons")); + } else if (p_what == NOTIFICATION_THEME_CHANGED) { Ref<Font> df_output_code = get_theme_font("output_source", "EditorFonts"); if (df_output_code.is_valid()) { @@ -75,8 +87,15 @@ void EditorLog::_notification(int p_what) { } } +void EditorLog::_set_collapse(bool p_collapse) { + collapse = p_collapse; + _rebuild_log(); +} + void EditorLog::_clear_request() { log->clear(); + messages.clear(); + _reset_message_counts(); tool_button->set_icon(Ref<Texture2D>()); } @@ -96,13 +115,81 @@ void EditorLog::clear() { _clear_request(); } -void EditorLog::copy() { - _copy_request(); +void EditorLog::_process_message(const String &p_msg, MessageType p_type) { + if (messages.size() > 0 && messages[messages.size() - 1].text == p_msg) { + // If previous message is the same as the new one, increase previous count rather than adding another + // instance to the messages list. + LogMessage &previous = messages.write[messages.size() - 1]; + previous.count++; + + _add_log_line(previous, collapse); + } else { + // Different message to the previous one received. + LogMessage message(p_msg, p_type); + _add_log_line(message); + messages.push_back(message); + } + + type_filter_map[p_type]->set_message_count(type_filter_map[p_type]->get_message_count() + 1); } void EditorLog::add_message(const String &p_msg, MessageType p_type) { - bool restore = p_type != MSG_TYPE_STD; - switch (p_type) { + // Make text split by new lines their own message. + // See #41321 for reasoning. At time of writing, multiple print()'s in running projects + // get grouped together and sent to the editor log as one message. This can mess with the + // search functionality (see the comments on the PR above for more details). This behaviour + // also matches that of other IDE's. + Vector<String> lines = p_msg.split("\n", false); + + for (int i = 0; i < lines.size(); i++) { + _process_message(lines[i], p_type); + } +} + +void EditorLog::set_tool_button(Button *p_tool_button) { + tool_button = p_tool_button; +} + +void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) { + EditorLog *self = (EditorLog *)p_self; + self->add_message(p_name, EditorLog::MSG_TYPE_EDITOR); +} + +void EditorLog::_rebuild_log() { + log->clear(); + + for (int msg_idx = 0; msg_idx < messages.size(); msg_idx++) { + LogMessage msg = messages[msg_idx]; + + if (collapse) { + // If collapsing, only log one instance of the message. + _add_log_line(msg); + } else { + // If not collapsing, log each instance on a line. + for (int i = 0; i < msg.count; i++) { + _add_log_line(msg); + } + } + } +} + +void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) { + // Only add the message to the log if it passes the filters. + bool filter_active = type_filter_map[p_message.type]->active; + String search_text = search_box->get_text(); + bool search_match = search_text == String() || p_message.text.findn(search_text) > -1; + + if (!filter_active || !search_match) { + return; + } + + if (p_replace_previous) { + // Remove last line if replacing, as it will be replace by the next added line. + log->remove_line(log->get_line_count() - 1); + log->increment_line_count(); + } + + switch (p_message.type) { case MSG_TYPE_STD: { } break; case MSG_TYPE_ERROR: { @@ -125,21 +212,43 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) { } break; } - log->add_text(p_msg); + // If collapsing, add the count of this message in bold at the start of the line. + if (collapse && p_message.count > 1) { + log->push_bold(); + log->add_text(vformat("(%s) ", itos(p_message.count))); + log->pop(); + } + + log->add_text(p_message.text); log->add_newline(); - if (restore) { + // Need to use pop() to exit out of the RichTextLabels current "push" stack. + // We only "push" in the above switch when message type != STD, so only pop when that is the case. + if (p_message.type != MSG_TYPE_STD) { log->pop(); } } -void EditorLog::set_tool_button(Button *p_tool_button) { - tool_button = p_tool_button; +void EditorLog::_set_filter_active(bool p_active, MessageType p_message_type) { + type_filter_map[p_message_type]->active = p_active; + _rebuild_log(); } -void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) { - EditorLog *self = (EditorLog *)p_self; - self->add_message(p_name, EditorLog::MSG_TYPE_EDITOR); +void EditorLog::_set_search_visible(bool p_visible) { + search_box->set_visible(p_visible); + if (p_visible) { + search_box->grab_focus(); + } +} + +void EditorLog::_search_changed(const String &p_text) { + _rebuild_log(); +} + +void EditorLog::_reset_message_counts() { + for (Map<MessageType, LogFilter *>::Element *E = type_filter_map.front(); E; E = E->next()) { + E->value()->set_message_count(0); + } } void EditorLog::_bind_methods() { @@ -148,37 +257,108 @@ void EditorLog::_bind_methods() { } EditorLog::EditorLog() { - VBoxContainer *vb = this; - - HBoxContainer *hb = memnew(HBoxContainer); - vb->add_child(hb); - title = memnew(Label); - title->set_text(TTR("Output:")); - title->set_h_size_flags(SIZE_EXPAND_FILL); - hb->add_child(title); - - copybutton = memnew(Button); - hb->add_child(copybutton); - copybutton->set_text(TTR("Copy")); - copybutton->set_shortcut(ED_SHORTCUT("editor/copy_output", TTR("Copy Selection"), KEY_MASK_CMD | KEY_C)); - copybutton->set_shortcut_context(this); - copybutton->connect("pressed", callable_mp(this, &EditorLog::_copy_request)); - - clearbutton = memnew(Button); - hb->add_child(clearbutton); - clearbutton->set_text(TTR("Clear")); - clearbutton->set_shortcut(ED_SHORTCUT("editor/clear_output", TTR("Clear Output"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_K)); - clearbutton->set_shortcut_context(this); - clearbutton->connect("pressed", callable_mp(this, &EditorLog::_clear_request)); + HBoxContainer *hb = this; + + VBoxContainer *vb_left = memnew(VBoxContainer); + vb_left->set_custom_minimum_size(Size2(0, 180) * EDSCALE); + vb_left->set_v_size_flags(SIZE_EXPAND_FILL); + vb_left->set_h_size_flags(SIZE_EXPAND_FILL); + hb->add_child(vb_left); + // Log - Rich Text Label. log = memnew(RichTextLabel); log->set_scroll_follow(true); log->set_selection_enabled(true); log->set_focus_mode(FOCUS_CLICK); - log->set_custom_minimum_size(Size2(0, 180) * EDSCALE); log->set_v_size_flags(SIZE_EXPAND_FILL); log->set_h_size_flags(SIZE_EXPAND_FILL); - vb->add_child(log); + vb_left->add_child(log); + + // Search box + search_box = memnew(LineEdit); + search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); + search_box->set_placeholder(TTR("Filter messages")); + search_box->set_right_icon(get_theme_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); + search_box->set_visible(true); + search_box->connect("text_changed", callable_mp(this, &EditorLog::_search_changed)); + vb_left->add_child(search_box); + + VBoxContainer *vb_right = memnew(VBoxContainer); + hb->add_child(vb_right); + + // Tools grid + HBoxContainer *hb_tools = memnew(HBoxContainer); + hb_tools->set_h_size_flags(SIZE_SHRINK_CENTER); + vb_right->add_child(hb_tools); + + // Clear. + clear_button = memnew(Button); + clear_button->set_flat(true); + clear_button->set_focus_mode(FOCUS_NONE); + clear_button->set_shortcut(ED_SHORTCUT("editor/clear_output", TTR("Clear Output"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_K)); + clear_button->set_shortcut_context(this); + clear_button->connect("pressed", callable_mp(this, &EditorLog::_clear_request)); + hb_tools->add_child(clear_button); + + // Copy. + copy_button = memnew(Button); + copy_button->set_flat(true); + copy_button->set_focus_mode(FOCUS_NONE); + copy_button->set_shortcut(ED_SHORTCUT("editor/copy_output", TTR("Copy Selection"), KEY_MASK_CMD | KEY_C)); + copy_button->set_shortcut_context(this); + copy_button->connect("pressed", callable_mp(this, &EditorLog::_copy_request)); + hb_tools->add_child(copy_button); + + // A second hbox to make a 2x2 grid of buttons. + HBoxContainer *hb_tools2 = memnew(HBoxContainer); + hb_tools2->set_h_size_flags(SIZE_SHRINK_CENTER); + vb_right->add_child(hb_tools2); + + // Collapse. + collapse_button = memnew(Button); + collapse_button->set_flat(true); + collapse_button->set_focus_mode(FOCUS_NONE); + collapse_button->set_tooltip(TTR("Collapse duplicate messages into one log entry. Shows number of occurences.")); + collapse_button->set_toggle_mode(true); + collapse_button->set_pressed(false); + collapse_button->connect("toggled", callable_mp(this, &EditorLog::_set_collapse)); + hb_tools2->add_child(collapse_button); + + // Show Search. + show_search_button = memnew(Button); + show_search_button->set_flat(true); + show_search_button->set_focus_mode(FOCUS_NONE); + show_search_button->set_toggle_mode(true); + show_search_button->set_pressed(true); + show_search_button->set_shortcut(ED_SHORTCUT("editor/open_search", TTR("Open the search box."), KEY_MASK_CMD | KEY_F)); + show_search_button->set_shortcut_context(this); + show_search_button->connect("toggled", callable_mp(this, &EditorLog::_set_search_visible)); + hb_tools2->add_child(show_search_button); + + // Message Type Filters. + vb_right->add_child(memnew(HSeparator)); + + LogFilter *std_filter = memnew(LogFilter(MSG_TYPE_STD)); + std_filter->initialize_button("Toggle visibility of standard output messages.", callable_mp(this, &EditorLog::_set_filter_active)); + vb_right->add_child(std_filter->toggle_button); + type_filter_map.insert(MSG_TYPE_STD, std_filter); + + LogFilter *error_filter = memnew(LogFilter(MSG_TYPE_ERROR)); + error_filter->initialize_button("Toggle visibility of errors.", callable_mp(this, &EditorLog::_set_filter_active)); + vb_right->add_child(error_filter->toggle_button); + type_filter_map.insert(MSG_TYPE_ERROR, error_filter); + + LogFilter *warning_filter = memnew(LogFilter(MSG_TYPE_WARNING)); + warning_filter->initialize_button("Toggle visibility of warnings.", callable_mp(this, &EditorLog::_set_filter_active)); + vb_right->add_child(warning_filter->toggle_button); + type_filter_map.insert(MSG_TYPE_WARNING, warning_filter); + + LogFilter *editor_filter = memnew(LogFilter(MSG_TYPE_EDITOR)); + editor_filter->initialize_button("Toggle visibility of editor messages.", callable_mp(this, &EditorLog::_set_filter_active)); + vb_right->add_child(editor_filter->toggle_button); + type_filter_map.insert(MSG_TYPE_EDITOR, editor_filter); + add_message(VERSION_FULL_NAME " (c) 2007-2021 Juan Linietsky, Ariel Manzur & Godot Contributors."); eh.errfunc = _error_handler; @@ -187,8 +367,6 @@ EditorLog::EditorLog() { current = Thread::get_caller_id(); - add_theme_constant_override("separation", get_theme_constant("separation", "VBoxContainer")); - EditorNode::get_undo_redo()->set_commit_notify_callback(_undo_redo_cbk, this); } @@ -197,4 +375,7 @@ void EditorLog::deinit() { } EditorLog::~EditorLog() { + for (Map<MessageType, LogFilter *>::Element *E = type_filter_map.front(); E; E = E->next()) { + memdelete(E->get()); + } } diff --git a/editor/editor_log.h b/editor/editor_log.h index 79dfb3ffaa..89d00d0fa0 100644 --- a/editor/editor_log.h +++ b/editor/editor_log.h @@ -36,19 +36,92 @@ #include "scene/gui/button.h" #include "scene/gui/control.h" #include "scene/gui/label.h" +#include "scene/gui/line_edit.h" #include "scene/gui/panel_container.h" #include "scene/gui/rich_text_label.h" #include "scene/gui/texture_button.h" #include "scene/gui/texture_rect.h" -class EditorLog : public VBoxContainer { - GDCLASS(EditorLog, VBoxContainer); +class EditorLog : public HBoxContainer { + GDCLASS(EditorLog, HBoxContainer); + +public: + enum MessageType { + MSG_TYPE_STD, + MSG_TYPE_ERROR, + MSG_TYPE_WARNING, + MSG_TYPE_EDITOR, + }; + +private: + struct LogMessage { + String text; + MessageType type; + int count = 1; + + LogMessage() {} + + LogMessage(const String p_text, MessageType p_type) : + text(p_text), + type(p_type) { + } + }; + + // Encapsulates all data and functionality regarding filters. + struct LogFilter { + private: + // Force usage of set method since it has functionality built-in. + int message_count = 0; + + public: + MessageType type; + Button *toggle_button = nullptr; + bool active = true; + + void initialize_button(const String &p_tooltip, Callable p_toggled_callback) { + toggle_button = memnew(Button); + toggle_button->set_toggle_mode(true); + toggle_button->set_pressed(true); + toggle_button->set_text(itos(message_count)); + toggle_button->set_tooltip(TTR(p_tooltip)); + // Don't tint the icon even when in "pressed" state. + toggle_button->add_theme_color_override("icon_color_pressed", Color(1, 1, 1, 1)); + toggle_button->set_focus_mode(FOCUS_NONE); + // When toggled call the callback and pass the MessageType this button is for. + toggle_button->connect("toggled", p_toggled_callback, varray(type)); + } + + int get_message_count() { + return message_count; + } + + void set_message_count(int p_count) { + message_count = p_count; + toggle_button->set_text(itos(message_count)); + } + + LogFilter(MessageType p_type) : + type(p_type) { + } + }; + + Vector<LogMessage> messages; + // Maps MessageTypes to LogFilters for convenient access and storage (don't need 1 member per filter). + Map<MessageType, LogFilter *> type_filter_map; - Button *clearbutton; - Button *copybutton; - Label *title; RichTextLabel *log; - HBoxContainer *title_hb; + + Button *clear_button; + Button *copy_button; + + Button *collapse_button; + bool collapse = false; + + Button *show_search_button; + LineEdit *search_box; + + // Reference to the "Output" button on the toolbar so we can update it's icon when + // Warnings or Errors are encounetered. Button *tool_button; static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type); @@ -62,26 +135,33 @@ class EditorLog : public VBoxContainer { void _copy_request(); static void _undo_redo_cbk(void *p_self, const String &p_name); + void _rebuild_log(); + void _add_log_line(LogMessage &p_message, bool p_replace_previous = false); + + void _set_filter_active(bool p_active, MessageType p_message_type); + void _set_search_visible(bool p_visible); + void _search_changed(const String &p_text); + + void _process_message(const String &p_msg, MessageType p_type); + void _reset_message_counts(); + + void _set_collapse(bool p_collapse); + protected: static void _bind_methods(); void _notification(int p_what); public: - enum MessageType { - MSG_TYPE_STD, - MSG_TYPE_ERROR, - MSG_TYPE_WARNING, - MSG_TYPE_EDITOR - }; - void add_message(const String &p_msg, MessageType p_type = MSG_TYPE_STD); void set_tool_button(Button *p_tool_button); void deinit(); void clear(); - void copy(); + EditorLog(); ~EditorLog(); }; +VARIANT_ENUM_CAST(EditorLog::MessageType); + #endif // EDITOR_LOG_H diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 1202de282c..ed670b1296 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -46,11 +46,13 @@ #include "core/string/print_string.h" #include "core/string/translation.h" #include "core/version.h" +#include "core/version_hash.gen.h" #include "main/main.h" #include "scene/gui/center_container.h" #include "scene/gui/control.h" #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" +#include "scene/gui/link_button.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" #include "scene/gui/panel_container.h" @@ -184,6 +186,9 @@ EditorNode *EditorNode::singleton = nullptr; +// The metadata key used to store and retrieve the version text to copy to the clipboard. +static const String META_TEXT_TO_COPY = "text_to_copy"; + void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames) { // Keep track of a list of "index sets," i.e. sets of indices // within disambiguated_scene_names which contain the same name. @@ -989,6 +994,10 @@ void EditorNode::_reload_project_settings() { void EditorNode::_vp_resized() { } +void EditorNode::_version_button_pressed() { + DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY)); +} + void EditorNode::_node_renamed() { if (get_inspector()) { get_inspector()->update_tree(); @@ -1399,8 +1408,9 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { } else { // The 3D editor may be disabled as a feature, but scenes can still be opened. // This check prevents the preview from regenerating in case those scenes are then saved. + // The preview will be generated if no feature profile is set (as the 3D editor is enabled by default). Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile(); - if (profile.is_valid() && !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)) { + if (!profile.is_valid() || !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)) { img = Node3DEditor::get_singleton()->get_editor_viewport(0)->get_viewport_node()->get_texture()->get_image(); } } @@ -4855,7 +4865,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { _scene_tab_closed(scene_tabs->get_hovered_tab()); } } else { - if ((mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_doubleclick()) || (mb->get_button_index() == MOUSE_BUTTON_MIDDLE && mb->is_pressed())) { + if ((mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_double_click()) || (mb->get_button_index() == MOUSE_BUTTON_MIDDLE && mb->is_pressed())) { _menu_option_confirm(FILE_NEW_SCENE, true); } } @@ -5559,6 +5569,8 @@ void EditorNode::_bind_methods() { ClassDB::bind_method("_screenshot", &EditorNode::_screenshot); ClassDB::bind_method("_save_screenshot", &EditorNode::_save_screenshot); + ClassDB::bind_method("_version_button_pressed", &EditorNode::_version_button_pressed); + ADD_SIGNAL(MethodInfo("play_pressed")); ADD_SIGNAL(MethodInfo("pause_pressed")); ADD_SIGNAL(MethodInfo("stop_pressed")); @@ -6616,11 +6628,31 @@ EditorNode::EditorNode() { bottom_panel_hb_editors->set_h_size_flags(Control::SIZE_EXPAND_FILL); bottom_panel_hb->add_child(bottom_panel_hb_editors); - version_label = memnew(Label); - version_label->set_text(VERSION_FULL_CONFIG); + VBoxContainer *version_info_vbc = memnew(VBoxContainer); + bottom_panel_hb->add_child(version_info_vbc); + + // Add a dummy control node for vertical spacing. + Control *v_spacer = memnew(Control); + version_info_vbc->add_child(v_spacer); + + version_btn = memnew(LinkButton); + version_btn->set_text(VERSION_FULL_CONFIG); + String hash = String(VERSION_HASH); + if (hash.length() != 0) { + hash = "." + hash.left(9); + } + // Set the text to copy in metadata as it slightly differs from the button's text. + version_btn->set_meta(META_TEXT_TO_COPY, "v" VERSION_FULL_BUILD + hash); // Fade out the version label to be less prominent, but still readable - version_label->set_self_modulate(Color(1, 1, 1, 0.6)); - bottom_panel_hb->add_child(version_label); + version_btn->set_self_modulate(Color(1, 1, 1, 0.65)); + version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); + version_btn->set_tooltip(TTR("Click to copy.")); + version_btn->connect("pressed", callable_mp(this, &EditorNode::_version_button_pressed)); + version_info_vbc->add_child(version_btn); + + // Add a dummy control node for horizontal spacing. + Control *h_spacer = memnew(Control); + bottom_panel_hb->add_child(h_spacer); bottom_panel_raise = memnew(Button); bottom_panel_raise->set_flat(true); diff --git a/editor/editor_node.h b/editor/editor_node.h index 7e16936f5d..d06851cb4f 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -40,6 +40,7 @@ #include "editor/inspector_dock.h" #include "editor/property_editor.h" #include "editor/scene_tree_dock.h" +#include "scene/gui/link_button.h" typedef void (*EditorNodeInitCallback)(); typedef void (*EditorPluginInitializeCallback)(); @@ -424,7 +425,7 @@ private: HBoxContainer *bottom_panel_hb; HBoxContainer *bottom_panel_hb_editors; VBoxContainer *bottom_panel_vb; - Label *version_label; + LinkButton *version_btn; Button *bottom_panel_raise; Tree *disk_changed_list; @@ -477,6 +478,7 @@ private: void _close_messages(); void _show_messages(); void _vp_resized(); + void _version_button_pressed(); int _save_external_resources(); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index eabcbacd9a..6b96cb0f5c 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -703,6 +703,14 @@ bool EditorPlugin::get_remove_list(List<Node *> *p_list) { void EditorPlugin::restore_global_state() {} void EditorPlugin::save_global_state() {} +void EditorPlugin::add_undo_redo_inspector_hook_callback(Callable p_callable) { + EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(p_callable); +} + +void EditorPlugin::remove_undo_redo_inspector_hook_callback(Callable p_callable) { + EditorNode::get_singleton()->get_editor_data().remove_undo_redo_inspector_hook_callback(p_callable); +} + void EditorPlugin::add_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser) { EditorTranslationParser::get_singleton()->add_parser(p_parser, EditorTranslationParser::CUSTOM); } @@ -862,6 +870,8 @@ void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("hide_bottom_panel"), &EditorPlugin::hide_bottom_panel); ClassDB::bind_method(D_METHOD("get_undo_redo"), &EditorPlugin::_get_undo_redo); + ClassDB::bind_method(D_METHOD("add_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::add_undo_redo_inspector_hook_callback); + ClassDB::bind_method(D_METHOD("remove_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::remove_undo_redo_inspector_hook_callback); ClassDB::bind_method(D_METHOD("queue_save_layout"), &EditorPlugin::queue_save_layout); ClassDB::bind_method(D_METHOD("add_translation_parser_plugin", "parser"), &EditorPlugin::add_translation_parser_plugin); ClassDB::bind_method(D_METHOD("remove_translation_parser_plugin", "parser"), &EditorPlugin::remove_translation_parser_plugin); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 67b163eabf..37412e5ebe 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -225,6 +225,9 @@ public: EditorInterface *get_editor_interface(); ScriptCreateDialog *get_script_create_dialog(); + void add_undo_redo_inspector_hook_callback(Callable p_callable); + void remove_undo_redo_inspector_hook_callback(Callable p_callable); + int update_overlays() const; void queue_save_layout(); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 4ea993af5b..47c0e31da6 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -38,7 +38,7 @@ #include "scene/main/window.h" #include "scene/resources/font.h" -///////////////////// NULL ///////////////////////// +///////////////////// Nil ///////////////////////// void EditorPropertyNil::update_property() { } @@ -929,7 +929,7 @@ EditorPropertyFloat::EditorPropertyFloat() { void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) { const Ref<InputEventMouseButton> mb = p_ev; if (mb.is_valid()) { - if (mb->is_doubleclick() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_double_click() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { _setup_spin(); } diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 1c94e65b66..7e450ed5fc 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -1138,14 +1138,14 @@ void EditorSettings::setup_language() { } void EditorSettings::setup_network() { - List<IP_Address> local_ip; + List<IPAddress> local_ip; IP::get_singleton()->get_local_addresses(&local_ip); String hint; String current = has_setting("network/debug/remote_host") ? get("network/debug/remote_host") : ""; String selected = "127.0.0.1"; // Check that current remote_host is a valid interface address and populate hints. - for (List<IP_Address>::Element *E = local_ip.front(); E; E = E->next()) { + for (List<IPAddress>::Element *E = local_ip.front(); E; E = E->next()) { String ip = E->get(); // link-local IPv6 addresses don't work, skipping them diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 8577ccb9db..dba53a9708 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -70,7 +70,6 @@ void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) { grabbing_spinner_dist_cache = 0; pre_grab_value = get_value(); grabbing_spinner = false; - grabbing_spinner_mouse_pos = Input::get_singleton()->get_mouse_position(); } } else { if (grabbing_spinner_attempt) { @@ -283,6 +282,8 @@ void EditorSpinSlider::_notification(int p_what) { Rect2 grabber_rect = Rect2(ofs + gofs, svofs + 1, grabber_w, 2 * EDSCALE); draw_rect(grabber_rect, c); + grabbing_spinner_mouse_pos = get_global_position() + grabber_rect.position + grabber_rect.size * 0.5; + bool display_grabber = (mouse_over_spin || mouse_over_grabber) && !grabbing_spinner && !value_input_popup->is_visible(); if (grabber->is_visible() != display_grabber) { if (display_grabber) { diff --git a/editor/fileserver/editor_file_server.h b/editor/fileserver/editor_file_server.h index 8ffd4f9692..d0405e0bb7 100644 --- a/editor/fileserver/editor_file_server.h +++ b/editor/fileserver/editor_file_server.h @@ -54,7 +54,7 @@ class EditorFileServer : public Object { bool quit = false; }; - Ref<TCP_Server> server; + Ref<TCPServer> server; Set<Thread *> to_wait; static void _close_client(ClientData *cd); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 0c3bdfb3a3..6cd45fdf97 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1465,6 +1465,10 @@ void FileSystemDock::_folder_removed(String p_folder) { } current_path->set_text(path); + EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->get_filesystem_path(path); + if (efd) { + efd->force_update(); + } } void FileSystemDock::_rename_operation_confirm() { diff --git a/editor/icons/CombineLines.svg b/editor/icons/CombineLines.svg new file mode 100644 index 0000000000..124814ae88 --- /dev/null +++ b/editor/icons/CombineLines.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 2v2h14V2zm7 5v2h7V7zm0 5v2h7v-2zM4.976 14V9h2l-1.5-2-1.5-2-1.5 2-1.5 2h2v5z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/LargeTexture.svg b/editor/icons/LargeTexture.svg deleted file mode 100644 index 137a761e1d..0000000000 --- a/editor/icons/LargeTexture.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v1 2h1v-2h2v-1zm11 0v1h2v2h1v-3zm-3 5v1h-1v1h-2v1h-1v1h-1v1h2 2 2 2v-2h-1v-1-1h-1v-1zm-8 6v2 1h3v-1h-2v-2zm13 0v2h-2v1h3v-1-2z" fill="#e0e0e0" fill-opacity=".99608"/></svg> diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 4bb56beaeb..96002400f3 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -1136,7 +1136,7 @@ Ref<Animation> ResourceImporterScene::import_animation_from_other_importer(Edito return importer->import_animation(p_path, p_flags, p_bake_fps); } -void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<uint8_t> &r_dst_lightmap_cache) { +void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches) { EditorSceneImporterMeshNode3D *src_mesh_node = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node); if (src_mesh_node) { //is mesh @@ -1216,7 +1216,28 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m n = n->get_parent_spatial(); } - //use xf as transform for mesh, and bake it + Vector<uint8_t> lightmap_cache; + src_mesh_node->get_mesh()->lightmap_unwrap_cached(xf, p_lightmap_texel_size, p_src_lightmap_cache, lightmap_cache); + + if (!lightmap_cache.is_empty()) { + if (r_lightmap_caches.is_empty()) { + r_lightmap_caches.push_back(lightmap_cache); + } else { + String new_md5 = String::md5(lightmap_cache.ptr()); // MD5 is stored at the beginning of the cache data + + for (int i = 0; i < r_lightmap_caches.size(); i++) { + String md5 = String::md5(r_lightmap_caches[i].ptr()); + if (new_md5 < md5) { + r_lightmap_caches.insert(i, lightmap_cache); + break; + } + + if (new_md5 == md5) { + break; + } + } + } + } } if (save_to_file != String()) { @@ -1265,7 +1286,7 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m } for (int i = 0; i < p_node->get_child_count(); i++) { - _generate_meshes(p_node->get_child(i), p_mesh_data, p_generate_lods, p_create_shadow_meshes, p_light_bake_mode, p_lightmap_texel_size, p_src_lightmap_cache, r_dst_lightmap_cache); + _generate_meshes(p_node->get_child(i), p_mesh_data, p_generate_lods, p_create_shadow_meshes, p_light_bake_mode, p_lightmap_texel_size, p_src_lightmap_cache, r_lightmap_caches); } } @@ -1433,7 +1454,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p float lightmap_texel_size = MAX(0.001, texel_size); Vector<uint8_t> src_lightmap_cache; - Vector<uint8_t> dst_lightmap_cache; + Vector<Vector<uint8_t>> mesh_lightmap_caches; { src_lightmap_cache = FileAccess::get_file_as_array(p_source_file + ".unwrap_cache", &err); @@ -1446,124 +1467,20 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p if (subresources.has("meshes")) { mesh_data = subresources["meshes"]; } - _generate_meshes(scene, mesh_data, gen_lods, create_shadow_meshes, LightBakeMode(light_bake_mode), lightmap_texel_size, src_lightmap_cache, dst_lightmap_cache); + _generate_meshes(scene, mesh_data, gen_lods, create_shadow_meshes, LightBakeMode(light_bake_mode), lightmap_texel_size, src_lightmap_cache, mesh_lightmap_caches); - if (dst_lightmap_cache.size()) { + if (mesh_lightmap_caches.size()) { FileAccessRef f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE); if (f) { - f->store_buffer(dst_lightmap_cache.ptr(), dst_lightmap_cache.size()); - } - } - err = OK; - -#if 0 - if (light_bake_mode == 2 /* || generate LOD */) { - Map<Ref<ArrayMesh>, Transform> meshes; - _find_meshes(scene, meshes); - - String file_id = src_path.get_file(); - String cache_file_path = base_path.plus_file(file_id + ".unwrap_cache"); - - Vector<unsigned char> cache_data; - - if (FileAccess::exists(cache_file_path)) { - Error err2; - FileAccess *file = FileAccess::open(cache_file_path, FileAccess::READ, &err2); - - if (err2) { - if (file) { - memdelete(file); - } - } else { - int cache_size = file->get_len(); - cache_data.resize(cache_size); - file->get_buffer(cache_data.ptrw(), cache_size); + 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()); } - } - - Map<String, unsigned int> used_unwraps; - - EditorProgress progress2("gen_lightmaps", TTR("Generating Lightmaps"), meshes.size()); - int step = 0; - for (Map<Ref<ArrayMesh>, Transform>::Element *E = meshes.front(); E; E = E->next()) { - Ref<ArrayMesh> mesh = E->key(); - String name = mesh->get_name(); - if (name == "") { //should not happen but.. - name = "Mesh " + itos(step); - } - - progress2.step(TTR("Generating for Mesh: ") + name + " (" + itos(step) + "/" + itos(meshes.size()) + ")", step); - - int *ret_cache_data = (int *)cache_data.ptrw(); - unsigned int ret_cache_size = cache_data.size(); - bool ret_used_cache = true; // Tell the unwrapper to use the cache - Error err2 = mesh->lightmap_unwrap_cached(ret_cache_data, ret_cache_size, ret_used_cache, E->get(), texel_size); - - if (err2 != OK) { - EditorNode::add_io_error("Mesh '" + name + "' failed lightmap generation. Please fix geometry."); - } else { -` String hash = String::md5((unsigned char *)ret_cache_data); - used_unwraps.insert(hash, ret_cache_size); - - if (!ret_used_cache) { - // Cache was not used, add the generated entry to the current cache - if (cache_data.is_empty()) { - cache_data.resize(4 + ret_cache_size); - int *data = (int *)cache_data.ptrw(); - data[0] = 1; - memcpy(&data[1], ret_cache_data, ret_cache_size); - } else { - int current_size = cache_data.size(); - cache_data.resize(cache_data.size() + ret_cache_size); - unsigned char *ptrw = cache_data.ptrw(); - memcpy(&ptrw[current_size], ret_cache_data, ret_cache_size); - int *data = (int *)ptrw; - data[0] += 1; - } - } - } - step++; - } - - Error err2; - FileAccess *file = FileAccess::open(cache_file_path, FileAccess::WRITE, &err2); - - if (err2) { - if (file) { - memdelete(file); - } - } else { - // Store number of entries - file->store_32(used_unwraps.size()); - - // Store cache entries - const int *cache = (int *)cache_data.ptr(); - unsigned int r_idx = 1; - for (int i = 0; i < cache[0]; ++i) { - unsigned char *entry_start = (unsigned char *)&cache[r_idx]; - String entry_hash = String::md5(entry_start); - if (used_unwraps.has(entry_hash)) { - unsigned int entry_size = used_unwraps[entry_hash]; - file->store_buffer(entry_start, entry_size); - } - - r_idx += 4; // hash - r_idx += 2; // size hint - - int vertex_count = cache[r_idx]; - r_idx += 1; // vertex count - r_idx += vertex_count; // vertex - r_idx += vertex_count * 2; // uvs - - int index_count = cache[r_idx]; - r_idx += 1; // index count - r_idx += index_count; // indices - } - - file->close(); + f->close(); } } -#endif + err = OK; progress.step(TTR("Running Custom Script..."), 2); diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 00039f2ac6..8cb84abce2 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -119,7 +119,7 @@ class ResourceImporterScene : public ResourceImporter { }; void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner); - void _generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<uint8_t> &r_dst_lightmap_cache); + void _generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches); void _add_shapes(Node *p_node, const List<Ref<Shape3D>> &p_shapes); public: diff --git a/editor/import/scene_importer_mesh.cpp b/editor/import/scene_importer_mesh.cpp index 28fdd4ddbd..bc7e8a1626 100644 --- a/editor/import/scene_importer_mesh.cpp +++ b/editor/import/scene_importer_mesh.cpp @@ -583,7 +583,7 @@ Ref<NavigationMesh> EditorSceneImporterMesh::create_navigation_mesh() { return nm; } -extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache); +extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y); struct EditorSceneImporterMeshLightmapSurface { Ref<Material> material; @@ -593,22 +593,24 @@ struct EditorSceneImporterMeshLightmapSurface { String name; }; -Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size) { +Error EditorSceneImporterMesh::lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache) { ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes."); - Vector<float> vertices; - Vector<float> normals; - Vector<int> indices; - Vector<float> uv; - Vector<Pair<int, int>> uv_indices; + LocalVector<float> vertices; + LocalVector<float> normals; + LocalVector<int> indices; + LocalVector<float> uv; + LocalVector<Pair<int, int>> uv_indices; Vector<EditorSceneImporterMeshLightmapSurface> lightmap_surfaces; // Keep only the scale - Transform transform = p_base_transform; - transform.origin = Vector3(); - transform.looking_at(Vector3(1, 0, 0), Vector3(0, 1, 0)); + Basis basis = p_base_transform.get_basis(); + Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length()); + + Transform transform; + transform.scale(scale); Basis normal_basis = transform.basis.inverse().transposed(); @@ -623,15 +625,10 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format); - Vector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX]; + PackedVector3Array rvertices = arrays[Mesh::ARRAY_VERTEX]; int vc = rvertices.size(); - const Vector3 *r = rvertices.ptr(); - - Vector<Vector3> rnormals = arrays[Mesh::ARRAY_NORMAL]; - - ERR_FAIL_COND_V_MSG(rnormals.size() == 0, ERR_UNAVAILABLE, "Normals are required for lightmap unwrap."); - const Vector3 *rn = rnormals.ptr(); + PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL]; int vertex_ofs = vertices.size() / 3; @@ -640,24 +637,29 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign uv_indices.resize(vertex_ofs + vc); for (int j = 0; j < vc; j++) { - Vector3 v = transform.xform(r[j]); - Vector3 n = normal_basis.xform(rn[j]).normalized(); - - vertices.write[(j + vertex_ofs) * 3 + 0] = v.x; - vertices.write[(j + vertex_ofs) * 3 + 1] = v.y; - vertices.write[(j + vertex_ofs) * 3 + 2] = v.z; - normals.write[(j + vertex_ofs) * 3 + 0] = n.x; - normals.write[(j + vertex_ofs) * 3 + 1] = n.y; - normals.write[(j + vertex_ofs) * 3 + 2] = n.z; - uv_indices.write[j + vertex_ofs] = Pair<int, int>(i, j); + Vector3 v = transform.xform(rvertices[j]); + Vector3 n = normal_basis.xform(rnormals[j]).normalized(); + + vertices[(j + vertex_ofs) * 3 + 0] = v.x; + vertices[(j + vertex_ofs) * 3 + 1] = v.y; + vertices[(j + vertex_ofs) * 3 + 2] = v.z; + normals[(j + vertex_ofs) * 3 + 0] = n.x; + normals[(j + vertex_ofs) * 3 + 1] = n.y; + normals[(j + vertex_ofs) * 3 + 2] = n.z; + uv_indices[j + vertex_ofs] = Pair<int, int>(i, j); } - Vector<int> rindices = arrays[Mesh::ARRAY_INDEX]; + PackedInt32Array rindices = arrays[Mesh::ARRAY_INDEX]; int ic = rindices.size(); + float eps = 1.19209290e-7F; // Taken from xatlas.h if (ic == 0) { for (int j = 0; j < vc / 3; j++) { - if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate()) { + Vector3 p0 = transform.xform(rvertices[j * 3 + 0]); + Vector3 p1 = transform.xform(rvertices[j * 3 + 1]); + Vector3 p2 = transform.xform(rvertices[j * 3 + 2]); + + if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) { continue; } @@ -667,15 +669,18 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign } } else { - const int *ri = rindices.ptr(); - for (int j = 0; j < ic / 3; j++) { - if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate()) { + Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]); + Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]); + Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]); + + if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) { continue; } - indices.push_back(vertex_ofs + ri[j * 3 + 0]); - indices.push_back(vertex_ofs + ri[j * 3 + 1]); - indices.push_back(vertex_ofs + ri[j * 3 + 2]); + + indices.push_back(vertex_ofs + rindices[j * 3 + 0]); + indices.push_back(vertex_ofs + rindices[j * 3 + 1]); + indices.push_back(vertex_ofs + rindices[j * 3 + 2]); } } @@ -684,6 +689,9 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign //unwrap + bool use_cache = true; // Used to request cache generation and to know if cache was used + uint8_t *gen_cache; + int gen_cache_size; float *gen_uvs; int *gen_vertices; int *gen_indices; @@ -692,7 +700,7 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign int size_x; int size_y; - bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y, r_cache_data, r_cache_size, r_used_cache); + bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), p_src_cache.ptr(), &use_cache, &gen_cache, &gen_cache_size, &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y); if (!ok) { return ERR_CANT_CREATE; @@ -702,7 +710,7 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign clear(); //create surfacetools for each surface.. - Vector<Ref<SurfaceTool>> surfaces_tools; + LocalVector<Ref<SurfaceTool>> surfaces_tools; for (int i = 0; i < lightmap_surfaces.size(); i++) { Ref<SurfaceTool> st; @@ -714,11 +722,12 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign } print_verbose("Mesh: Gen indices: " + itos(gen_index_count)); + //go through all indices for (int i = 0; i < gen_index_count; i += 3) { - ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], uv_indices.size(), ERR_BUG); - ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_indices.size(), ERR_BUG); - ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], (int)uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], (int)uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], (int)uv_indices.size(), ERR_BUG); ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG); @@ -728,49 +737,54 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second]; if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_COLOR) { - surfaces_tools.write[surface]->set_color(v.color); + surfaces_tools[surface]->set_color(v.color); } if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TEX_UV) { - surfaces_tools.write[surface]->set_uv(v.uv); + surfaces_tools[surface]->set_uv(v.uv); } if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_NORMAL) { - surfaces_tools.write[surface]->set_normal(v.normal); + surfaces_tools[surface]->set_normal(v.normal); } if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TANGENT) { Plane t; t.normal = v.tangent; t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1; - surfaces_tools.write[surface]->set_tangent(t); + surfaces_tools[surface]->set_tangent(t); } if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_BONES) { - surfaces_tools.write[surface]->set_bones(v.bones); + surfaces_tools[surface]->set_bones(v.bones); } if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_WEIGHTS) { - surfaces_tools.write[surface]->set_weights(v.weights); + surfaces_tools[surface]->set_weights(v.weights); } Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]); - surfaces_tools.write[surface]->set_uv2(uv2); + surfaces_tools[surface]->set_uv2(uv2); - surfaces_tools.write[surface]->add_vertex(v.vertex); + surfaces_tools[surface]->add_vertex(v.vertex); } } //generate surfaces - - for (int i = 0; i < surfaces_tools.size(); i++) { - surfaces_tools.write[i]->index(); - Array arrays = surfaces_tools.write[i]->commit_to_arrays(); - add_surface(surfaces_tools.write[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools.write[i]->get_material(), surfaces_tools.write[i]->get_meta("name")); + for (unsigned int i = 0; i < surfaces_tools.size(); i++) { + surfaces_tools[i]->index(); + Array arrays = surfaces_tools[i]->commit_to_arrays(); + add_surface(surfaces_tools[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools[i]->get_material(), surfaces_tools[i]->get_meta("name")); } set_lightmap_size_hint(Size2(size_x, size_y)); - if (!r_used_cache) { - //free stuff - ::free(gen_vertices); - ::free(gen_indices); - ::free(gen_uvs); + if (gen_cache_size > 0) { + r_dst_cache.resize(gen_cache_size); + memcpy(r_dst_cache.ptrw(), gen_cache, gen_cache_size); + memfree(gen_cache); + } + + if (!use_cache) { + // Cache was not used, free the buffers + memfree(gen_vertices); + memfree(gen_indices); + memfree(gen_uvs); } return OK; diff --git a/editor/import/scene_importer_mesh.h b/editor/import/scene_importer_mesh.h index 3326fab55d..b3e8137e0a 100644 --- a/editor/import/scene_importer_mesh.h +++ b/editor/import/scene_importer_mesh.h @@ -105,7 +105,7 @@ public: Vector<Ref<Shape3D>> convex_decompose() const; Ref<Shape3D> create_trimesh_shape() const; Ref<NavigationMesh> create_navigation_mesh(); - Error lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size); + Error lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache); void set_lightmap_size_hint(const Size2i &p_size); Size2i get_lightmap_size_hint() const; diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp index 3553450672..3b54b30b54 100644 --- a/editor/plugins/audio_stream_editor_plugin.cpp +++ b/editor/plugins/audio_stream_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/io/resource_loader.h" +#include "core/os/keyboard.h" #include "editor/audio_stream_preview.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" @@ -144,23 +145,26 @@ void AudioStreamEditor::_draw_indicator() { Rect2 rect = _preview->get_rect(); float len = stream->get_length(); float ofs_x = _current / len * rect.size.width; - _indicator->draw_line(Point2(ofs_x, 0), Point2(ofs_x, rect.size.height), get_theme_color("accent_color", "Editor"), 1); + const Color color = get_theme_color("accent_color", "Editor"); + _indicator->draw_line(Point2(ofs_x, 0), Point2(ofs_x, rect.size.height), color, Math::round(2 * EDSCALE)); + _indicator->draw_texture( + get_theme_icon("TimelineIndicator", "EditorIcons"), + Point2(ofs_x - get_theme_icon("TimelineIndicator", "EditorIcons")->get_width() * 0.5, 0), + color); _current_label->set_text(String::num(_current, 2).pad_decimals(2) + " /"); } void AudioStreamEditor::_on_input_indicator(Ref<InputEvent> p_event) { - Ref<InputEventMouseButton> mb = p_event; - - if (mb.is_valid()) { + const Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (mb->is_pressed()) { _seek_to(mb->get_position().x); } _dragging = mb->is_pressed(); } - Ref<InputEventMouseMotion> mm = p_event; - + const Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { if (_dragging) { _seek_to(mm->get_position().x); @@ -228,6 +232,7 @@ AudioStreamEditor::AudioStreamEditor() { hbox->add_child(_play_button); _play_button->set_focus_mode(Control::FOCUS_NONE); _play_button->connect("pressed", callable_mp(this, &AudioStreamEditor::_play)); + _play_button->set_shortcut(ED_SHORTCUT("inspector/audio_preview_play_pause", TTR("Audio Preview Play/Pause"), KEY_SPACE)); _stop_button = memnew(Button); _stop_button->set_flat(true); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index bf1d87cada..852c615dc8 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1654,7 +1654,7 @@ bool CanvasItemEditor::_gui_input_open_scene_on_double_click(const Ref<InputEven Ref<InputEventMouseButton> b = p_event; // Open a sub-scene on double-click - if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed() && b->is_doubleclick() && tool == TOOL_SELECT) { + if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed() && b->is_double_click() && tool == TOOL_SELECT) { List<CanvasItem *> selection = _get_edited_canvas_items(); if (selection.size() == 1) { CanvasItem *canvas_item = selection[0]; @@ -6521,8 +6521,7 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian type == "CurveTexture" || type == "GradientTexture" || type == "StreamTexture2D" || - type == "AtlasTexture" || - type == "LargeTexture") { + type == "AtlasTexture") { Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(*res)); if (!texture.is_valid()) { continue; diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index d3e5854786..a319a595c7 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -81,7 +81,6 @@ bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const { Ref<Texture2D> EditorTexturePreviewPlugin::generate(const RES &p_from, const Size2 &p_size) const { Ref<Image> img; Ref<AtlasTexture> atex = p_from; - Ref<LargeTexture> ltex = p_from; if (atex.is_valid()) { Ref<Texture2D> tex = atex->get_atlas(); if (!tex.is_valid()) { @@ -94,8 +93,6 @@ Ref<Texture2D> EditorTexturePreviewPlugin::generate(const RES &p_from, const Siz } img = atlas->get_rect(atex->get_region()); - } else if (ltex.is_valid()) { - img = ltex->to_image(); } else { Ref<Texture2D> tex = p_from; if (tex.is_valid()) { @@ -507,6 +504,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size Color keyword_color = EditorSettings::get_singleton()->get("text_editor/highlighting/keyword_color"); Color text_color = EditorSettings::get_singleton()->get("text_editor/highlighting/text_color"); Color symbol_color = EditorSettings::get_singleton()->get("text_editor/highlighting/symbol_color"); + Color comment_color = EditorSettings::get_singleton()->get("text_editor/highlighting/comment_color"); if (bg_color.a == 0) { bg_color = Color(0, 0, 0, 0); @@ -526,33 +524,42 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size bool prev_is_text = false; bool in_keyword = false; + bool in_comment = false; for (int i = 0; i < code.length(); i++) { char32_t c = code[i]; if (c > 32) { if (col < thumbnail_size) { Color color = text_color; - if (c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t')) { - //make symbol a little visible - color = symbol_color; - in_keyword = false; - } else if (!prev_is_text && _is_text_char(c)) { - int pos = i; + if (c == '#') { + in_comment = true; + } - while (_is_text_char(code[pos])) { - pos++; - } - String word = code.substr(i, pos - i); - if (keywords.has(word)) { - in_keyword = true; + if (in_comment) { + color = comment_color; + } else { + if (c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t')) { + //make symbol a little visible + color = symbol_color; + in_keyword = false; + } else if (!prev_is_text && _is_text_char(c)) { + int pos = i; + + while (_is_text_char(code[pos])) { + pos++; + } + String word = code.substr(i, pos - i); + if (keywords.has(word)) { + in_keyword = true; + } + + } else if (!_is_text_char(c)) { + in_keyword = false; } - } else if (!_is_text_char(c)) { - in_keyword = false; - } - - if (in_keyword) { - color = keyword_color; + if (in_keyword) { + color = keyword_color; + } } Color ul = color; @@ -562,11 +569,14 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size prev_is_text = _is_text_char(c); } + col++; } else { prev_is_text = false; in_keyword = false; if (c == '\n') { + in_comment = false; + col = x0; line++; if (line >= available_height / 2) { @@ -574,9 +584,10 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size } } else if (c == '\t') { col += 3; + } else { + col++; } } - col++; } post_process_preview(img); diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp index 433a5ae51c..89d6aaa5f9 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp @@ -346,7 +346,7 @@ void GPUParticles3DEditor::_generate_emission_points() { { uint8_t *iw = point_img.ptrw(); - zeromem(iw, w * h * 3 * sizeof(float)); + memset(iw, 0, w * h * 3 * sizeof(float)); const Vector3 *r = points.ptr(); float *wf = (float *)iw; for (int i = 0; i < point_count; i++) { @@ -374,7 +374,7 @@ void GPUParticles3DEditor::_generate_emission_points() { { uint8_t *iw = point_img2.ptrw(); - zeromem(iw, w * h * 3 * sizeof(float)); + memset(iw, 0, w * h * 3 * sizeof(float)); const Vector3 *r = normals.ptr(); float *wf = (float *)iw; for (int i = 0; i < point_count; i++) { diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 9a00d68447..bc33d03d8b 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -698,7 +698,7 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { // Do not try to save internal scripts, but prompt to save in-memory // scripts which are not saved to disk yet (have empty path). if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) { - _menu_option(FILE_SAVE); + _save_current_script(); } } if (script.is_valid()) { @@ -1096,6 +1096,59 @@ bool ScriptEditor::is_scripts_panel_toggled() { return list_split->is_visible(); } +void ScriptEditor::_save_current_script() { + ScriptEditorBase *current = _get_current_editor(); + + if (_test_script_times_on_disk()) { + return; + } + + if (trim_trailing_whitespace_on_save) { + current->trim_trailing_whitespace(); + } + + current->insert_final_newline(); + + if (convert_indent_on_save) { + if (use_space_indentation) { + current->convert_indent_to_spaces(); + } else { + current->convert_indent_to_tabs(); + } + } + + RES resource = current->get_edited_resource(); + Ref<TextFile> text_file = resource; + Ref<Script> script = resource; + + if (text_file != nullptr) { + current->apply_code(); + _save_text_file(text_file, text_file->get_path()); + return; + } + + if (script != nullptr) { + const Vector<DocData::ClassDoc> &documentations = script->get_documentation(); + for (int j = 0; j < documentations.size(); j++) { + const DocData::ClassDoc &doc = documentations.get(j); + if (EditorHelp::get_doc_data()->has_doc(doc.name)) { + EditorHelp::get_doc_data()->remove_doc(doc.name); + } + } + } + + editor->save_resource(resource); + + if (script != nullptr) { + const Vector<DocData::ClassDoc> &documentations = script->get_documentation(); + for (int j = 0; j < documentations.size(); j++) { + const DocData::ClassDoc &doc = documentations.get(j); + EditorHelp::get_doc_data()->add_doc(doc); + update_doc(doc.name); + } + } +} + void ScriptEditor::_menu_option(int p_option) { ScriptEditorBase *current = _get_current_editor(); switch (p_option) { @@ -1224,55 +1277,7 @@ void ScriptEditor::_menu_option(int p_option) { if (current) { switch (p_option) { case FILE_SAVE: { - if (_test_script_times_on_disk()) { - return; - } - - if (trim_trailing_whitespace_on_save) { - current->trim_trailing_whitespace(); - } - - current->insert_final_newline(); - - if (convert_indent_on_save) { - if (use_space_indentation) { - current->convert_indent_to_spaces(); - } else { - current->convert_indent_to_tabs(); - } - } - - RES resource = current->get_edited_resource(); - Ref<TextFile> text_file = resource; - Ref<Script> script = resource; - - if (text_file != nullptr) { - current->apply_code(); - _save_text_file(text_file, text_file->get_path()); - break; - } - - if (script != nullptr) { - const Vector<DocData::ClassDoc> &documentations = script->get_documentation(); - for (int j = 0; j < documentations.size(); j++) { - const DocData::ClassDoc &doc = documentations.get(j); - if (EditorHelp::get_doc_data()->has_doc(doc.name)) { - EditorHelp::get_doc_data()->remove_doc(doc.name); - } - } - } - - editor->save_resource(resource); - - if (script != nullptr) { - const Vector<DocData::ClassDoc> &documentations = script->get_documentation(); - for (int j = 0; j < documentations.size(); j++) { - const DocData::ClassDoc &doc = documentations.get(j); - EditorHelp::get_doc_data()->add_doc(doc); - update_doc(doc.name); - } - } - + _save_current_script(); } break; case FILE_SAVE_AS: { if (trim_trailing_whitespace_on_save) { @@ -2434,6 +2439,9 @@ void ScriptEditor::_add_callback(Object *p_obj, const String &p_function, const script_list->select(script_list->find_metadata(i)); + // Save the current script so the changes can be picked up by an external editor. + _save_current_script(); + break; } } diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index b2172e7f10..c70fd2e555 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -312,6 +312,7 @@ class ScriptEditor : public PanelContainer { String _get_debug_tooltip(const String &p_text, Node *_se); + void _save_current_script(); void _resave_scripts(const String &p_str); void _reload_scripts(); diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index 253f8878d2..ecf7370834 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -143,7 +143,7 @@ TextureEditor::~TextureEditor() { // bool EditorInspectorPluginTexture::can_handle(Object *p_object) { - return Object::cast_to<ImageTexture>(p_object) != nullptr || Object::cast_to<AtlasTexture>(p_object) != nullptr || Object::cast_to<StreamTexture2D>(p_object) != nullptr || Object::cast_to<LargeTexture>(p_object) != nullptr || Object::cast_to<AnimatedTexture>(p_object) != nullptr; + return Object::cast_to<ImageTexture>(p_object) != nullptr || Object::cast_to<AtlasTexture>(p_object) != nullptr || Object::cast_to<StreamTexture2D>(p_object) != nullptr || Object::cast_to<AnimatedTexture>(p_object) != nullptr; } void EditorInspectorPluginTexture::parse_begin(Object *p_object) { diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index bd721244ea..1d6ff92e0c 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -573,6 +573,7 @@ void TileMapEditor::_update_palette() { entries2.sort_custom<SwapComparator>(); Ref<Texture2D> tex = tileset->tile_get_texture(sel_tile); + Color modulate = tileset->tile_get_modulate(sel_tile); for (int i = 0; i < entries2.size(); i++) { manual_palette->add_item(String()); @@ -588,6 +589,7 @@ void TileMapEditor::_update_palette() { } manual_palette->set_item_icon(manual_palette->get_item_count() - 1, tex); + manual_palette->set_item_icon_modulate(manual_palette->get_item_count() - 1, modulate); } manual_palette->set_item_metadata(manual_palette->get_item_count() - 1, entries2[i]); @@ -658,11 +660,15 @@ Vector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool erase, } // Check if the tile variation is the same - Vector2 prev_position = node->get_cell_autotile_coord(p_start.x, p_start.y); if (ids.size() == 1 && ids[0] == prev_id) { int current = manual_palette->get_current(); - Vector2 position = manual_palette->get_item_metadata(current); - if (prev_position == position) { + if (current == -1) { + // Same ID, no variation selected, nothing to change + return Vector<Vector2>(); + } + Vector2 prev_autotile_coord = node->get_cell_autotile_coord(p_start.x, p_start.y); + Vector2 autotile_coord = manual_palette->get_item_metadata(current); + if (autotile_coord == prev_autotile_coord) { // Same ID and variation, nothing to change return Vector<Vector2>(); } diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index feaf609557..f683c4b10d 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -319,7 +319,7 @@ void TileSetEditor::_notification(int p_what) { tool_editmode[EDITMODE_NAVIGATION]->set_icon(get_theme_icon("Navigation2D", "EditorIcons")); tool_editmode[EDITMODE_BITMASK]->set_icon(get_theme_icon("PackedDataContainer", "EditorIcons")); tool_editmode[EDITMODE_PRIORITY]->set_icon(get_theme_icon("MaterialPreviewLight1", "EditorIcons")); - tool_editmode[EDITMODE_ICON]->set_icon(get_theme_icon("LargeTexture", "EditorIcons")); + tool_editmode[EDITMODE_ICON]->set_icon(get_theme_icon("Image", "EditorIcons")); tool_editmode[EDITMODE_Z_INDEX]->set_icon(get_theme_icon("Sort", "EditorIcons")); scroll->add_theme_style_override("bg", get_theme_stylebox("bg", "Tree")); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index e51e8ee82e..2b75144ac7 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -1762,7 +1762,7 @@ void ProjectList::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) { emit_signal(SIGNAL_SELECTION_CHANGED); - if (!mb->get_control() && mb->is_doubleclick()) { + if (!mb->get_control() && mb->is_double_click()) { emit_signal(SIGNAL_PROJECT_ASK_OPEN); } } @@ -2371,6 +2371,7 @@ void ProjectManager::_on_search_term_changed(const String &p_term) { void ProjectManager::_bind_methods() { ClassDB::bind_method("_unhandled_key_input", &ProjectManager::_unhandled_key_input); ClassDB::bind_method("_update_project_buttons", &ProjectManager::_update_project_buttons); + ClassDB::bind_method("_version_button_pressed", &ProjectManager::_version_button_pressed); } void ProjectManager::_open_asset_library() { @@ -2378,6 +2379,10 @@ void ProjectManager::_open_asset_library() { tabs->set_current_tab(1); } +void ProjectManager::_version_button_pressed() { + DisplayServer::get_singleton()->clipboard_set(version_btn->get_text()); +} + ProjectManager::ProjectManager() { // load settings if (!EditorSettings::get_singleton()) { @@ -2601,15 +2606,30 @@ ProjectManager::ProjectManager() { settings_hb->set_h_grow_direction(Control::GROW_DIRECTION_BEGIN); settings_hb->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT); - Label *version_label = memnew(Label); + // A VBoxContainer that contains a dummy Control node to adjust the LinkButton's vertical position. + VBoxContainer *spacer_vb = memnew(VBoxContainer); + settings_hb->add_child(spacer_vb); + + Control *v_spacer = memnew(Control); + spacer_vb->add_child(v_spacer); + + version_btn = memnew(LinkButton); String hash = String(VERSION_HASH); if (hash.length() != 0) { hash = "." + hash.left(9); } - version_label->set_text("v" VERSION_FULL_BUILD "" + hash); - version_label->set_self_modulate(Color(1, 1, 1, 0.6)); - version_label->set_align(Label::ALIGN_CENTER); - settings_hb->add_child(version_label); + version_btn->set_text("v" VERSION_FULL_BUILD + hash); + // Fade the version label to be less prominent, but still readable. + version_btn->set_self_modulate(Color(1, 1, 1, 0.6)); + version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); + version_btn->set_tooltip(TTR("Click to copy.")); + version_btn->connect("pressed", callable_mp(this, &ProjectManager::_version_button_pressed)); + spacer_vb->add_child(version_btn); + + // Add a small horizontal spacer between the version and language buttons + // to distinguish them. + Control *h_spacer = memnew(Control); + settings_hb->add_child(h_spacer); language_btn = memnew(OptionButton); language_btn->set_flat(true); diff --git a/editor/project_manager.h b/editor/project_manager.h index a66b7c4ab6..0fc69bd313 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -89,6 +89,7 @@ class ProjectManager : public Control { ProjectDialog *npdialog; OptionButton *language_btn; + LinkButton *version_btn; void _open_asset_library(); void _scan_projects(); @@ -123,6 +124,7 @@ class ProjectManager : public Control { void _unhandled_key_input(const Ref<InputEvent> &p_ev); void _files_dropped(PackedStringArray p_files, int p_screen); + void _version_button_pressed(); void _on_order_option_changed(int p_idx); void _on_tab_changed(int p_tab); void _on_search_term_changed(const String &p_term); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index a6d1a118b8..49c231de69 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "core/input/input.h" #include "core/io/resource_saver.h" +#include "core/object/message_queue.h" #include "core/os/keyboard.h" #include "editor/debugger/editor_debugger_node.h" #include "editor/editor_feature_profile.h" @@ -3016,7 +3017,16 @@ void SceneTreeDock::_bind_methods() { ADD_SIGNAL(MethodInfo("node_created", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); } +SceneTreeDock *SceneTreeDock::singleton = nullptr; + +void SceneTreeDock::_update_configuration_warning() { + if (singleton) { + MessageQueue::get_singleton()->push_callable(callable_mp(singleton->scene_tree, &SceneTreeEditor::update_warning)); + } +} + SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data) { + singleton = this; set_name("Scene"); editor = p_editor; edited_scene = nullptr; @@ -3207,9 +3217,12 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true); EDITOR_DEF("interface/editors/derive_script_globals_by_name", true); EDITOR_DEF("_use_favorites_root_selection", false); + + Resource::_update_configuration_warning = _update_configuration_warning; } SceneTreeDock::~SceneTreeDock() { + singleton = nullptr; if (!node_clipboard.is_empty()) { _clear_clipboard(); } diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index aa62c93cb5..53f31375f8 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -244,6 +244,9 @@ class SceneTreeDock : public VBoxContainer { bool profile_allow_editing; bool profile_allow_script_editing; + static SceneTreeDock *singleton; + static void _update_configuration_warning(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index b4f7eda391..d0bf83eb2d 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -540,6 +540,10 @@ void SceneTreeEditor::_update_tree(bool p_scroll_to_selected) { return; } + if (tree->is_editing()) { + return; + } + updating_tree = true; tree->clear(); if (get_scene_node()) { @@ -665,7 +669,7 @@ void SceneTreeEditor::_notification(int p_what) { get_tree()->connect("tree_process_mode_changed", callable_mp(this, &SceneTreeEditor::_tree_process_mode_changed)); get_tree()->connect("node_removed", callable_mp(this, &SceneTreeEditor::_node_removed)); get_tree()->connect("node_renamed", callable_mp(this, &SceneTreeEditor::_node_renamed)); - get_tree()->connect("node_configuration_warning_changed", callable_mp(this, &SceneTreeEditor::_warning_changed)); + get_tree()->connect("node_configuration_warning_changed", callable_mp(this, &SceneTreeEditor::_warning_changed), varray(), CONNECT_DEFERRED); tree->connect("item_collapsed", callable_mp(this, &SceneTreeEditor::_cell_collapsed)); @@ -1102,6 +1106,9 @@ void SceneTreeEditor::_rmb_select(const Vector2 &p_pos) { emit_signal("rmb_pressed", tree->get_screen_transform().xform(p_pos)); } +void SceneTreeEditor::update_warning() { + _warning_changed(nullptr); +} void SceneTreeEditor::_warning_changed(Node *p_for_node) { //should use a timer update_timer->start(); diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h index b4c27b7c1f..acd49e8d92 100644 --- a/editor/scene_tree_editor.h +++ b/editor/scene_tree_editor.h @@ -157,6 +157,8 @@ public: Tree *get_scene_tree() { return tree; } + void update_warning(); + SceneTreeEditor(bool p_label = true, bool p_can_rename = false, bool p_can_open_instance = false); ~SceneTreeEditor(); }; diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index 81af4996ed..541ec224b9 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -270,16 +270,17 @@ void EditorSettingsDialog::_update_shortcuts() { Array events; // Need to get the list of events into an array so it can be set as metadata on the item. Vector<String> event_strings; - List<Ref<InputEvent>> defaults = InputMap::get_singleton()->get_builtins().find(action_name).value(); - // Remove all non-key events from the defaults. - for (List<Ref<InputEvent>>::Element *I = defaults.front(); I; I = I->next()) { + List<Ref<InputEvent>> all_default_events = InputMap::get_singleton()->get_builtins().find(action_name).value(); + List<Ref<InputEventKey>> key_default_events; + // Remove all non-key events from the defaults. Only check keys, since we are in the editor. + for (List<Ref<InputEvent>>::Element *I = all_default_events.front(); I; I = I->next()) { Ref<InputEventKey> k = I->get(); - if (k.is_null()) { - I->erase(); + if (k.is_valid()) { + key_default_events.push_back(k); } } - bool same_as_defaults = defaults.size() == action.inputs.size(); // Initially this is set to just whether the arrays are equal. Later we check the events if needed. + bool same_as_defaults = key_default_events.size() == action.inputs.size(); // Initially this is set to just whether the arrays are equal. Later we check the events if needed. int count = 0; for (List<Ref<InputEvent>>::Element *I = action.inputs.front(); I; I = I->next()) { @@ -288,12 +289,8 @@ void EditorSettingsDialog::_update_shortcuts() { event_strings.push_back(I->get()->as_text()); // Only check if the events have been the same so far - once one fails, we don't need to check any more. - if (same_as_defaults) { - Ref<InputEventKey> k = defaults[count]; - // Only check keys, since we are in the editor. - if (k.is_valid() && !defaults[count]->shortcut_match(I->get())) { - same_as_defaults = false; - } + if (same_as_defaults && !key_default_events[count]->shortcut_match(I->get())) { + same_as_defaults = false; } count++; } |