diff options
Diffstat (limited to 'editor')
27 files changed, 618 insertions, 288 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/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_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..9f188b53c4 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,83 @@ 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(); + } else { + log->add_newline(); + } + + switch (p_message.type) { case MSG_TYPE_STD: { } break; case MSG_TYPE_ERROR: { @@ -125,21 +214,42 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) { } break; } - log->add_text(p_msg); - log->add_newline(); + // 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(); + } - if (restore) { + log->add_text(p_message.text); + + // 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 +258,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 +368,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 +376,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 6137617564..8eeabf9cfd 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")); @@ -6617,11 +6629,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 652deb1804..47c0e31da6 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -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_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/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/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 6ac47595dc..261621e10a 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]; 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/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/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++; } |