diff options
Diffstat (limited to 'editor')
45 files changed, 850 insertions, 405 deletions
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 7c403b7523..ecc465ef64 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -2134,10 +2134,9 @@ void AnimationTrackEdit::_notification(int p_what) { get_theme_icon(SNAME("InterpLinearAngle"), SNAME("EditorIcons")), get_theme_icon(SNAME("InterpCubicAngle"), SNAME("EditorIcons")), }; - Ref<Texture2D> cont_icon[4] = { + Ref<Texture2D> cont_icon[3] = { get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), - get_theme_icon(SNAME("TrackTrigger"), SNAME("EditorIcons")), get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")) }; @@ -2829,7 +2828,6 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) { menu->clear(); menu->add_icon_item(get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), TTR("Continuous"), MENU_CALL_MODE_CONTINUOUS); menu->add_icon_item(get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), TTR("Discrete"), MENU_CALL_MODE_DISCRETE); - menu->add_icon_item(get_theme_icon(SNAME("TrackTrigger"), SNAME("EditorIcons")), TTR("Trigger"), MENU_CALL_MODE_TRIGGER); menu->add_icon_item(get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")), TTR("Capture"), MENU_CALL_MODE_CAPTURE); menu->reset_size(); @@ -3194,7 +3192,6 @@ void AnimationTrackEdit::_menu_selected(int p_index) { switch (p_index) { case MENU_CALL_MODE_CONTINUOUS: case MENU_CALL_MODE_DISCRETE: - case MENU_CALL_MODE_TRIGGER: case MENU_CALL_MODE_CAPTURE: { Animation::UpdateMode update_mode = Animation::UpdateMode(p_index); Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); @@ -4322,10 +4319,6 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD h.type == Variant::TRANSFORM3D) { update_mode = Animation::UPDATE_CONTINUOUS; } - - if (h.usage & PROPERTY_USAGE_ANIMATE_AS_TRIGGER) { - update_mode = Animation::UPDATE_TRIGGER; - } } } @@ -4953,10 +4946,6 @@ void AnimationTrackEditor::_new_track_property_selected(String p_name) { h.type == Variant::TRANSFORM3D) { update_mode = Animation::UPDATE_CONTINUOUS; } - - if (h.usage & PROPERTY_USAGE_ANIMATE_AS_TRIGGER) { - update_mode = Animation::UPDATE_TRIGGER; - } } undo_redo->create_action(TTR("Add Track")); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index dc0c4abe5f..4b50424f39 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -138,7 +138,6 @@ class AnimationTrackEdit : public Control { enum { MENU_CALL_MODE_CONTINUOUS, MENU_CALL_MODE_DISCRETE, - MENU_CALL_MODE_TRIGGER, MENU_CALL_MODE_CAPTURE, MENU_INTERPOLATION_NEAREST, MENU_INTERPOLATION_LINEAR, diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 65cb083ac7..7ae0db7b48 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -33,6 +33,7 @@ #include "core/input/input.h" #include "core/os/keyboard.h" #include "core/string/string_builder.h" +#include "core/templates/pair.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/plugins/script_editor_plugin.h" @@ -872,6 +873,10 @@ void CodeTextEditor::_reset_zoom() { } void CodeTextEditor::_line_col_changed() { + if (!code_complete_timer->is_stopped() && code_complete_timer_line != text_editor->get_caret_line()) { + code_complete_timer->stop(); + } + String line = text_editor->get_line(text_editor->get_caret_line()); int positional_column = 0; @@ -901,6 +906,7 @@ void CodeTextEditor::_line_col_changed() { void CodeTextEditor::_text_changed() { if (text_editor->is_insert_text_operation()) { + code_complete_timer_line = text_editor->get_caret_line(); code_complete_timer->start(); } @@ -1290,90 +1296,98 @@ void CodeTextEditor::convert_case(CaseStyle p_case) { void CodeTextEditor::move_lines_up() { text_editor->begin_complex_operation(); - Vector<int> carets_to_remove; - Vector<int> caret_edit_order = text_editor->get_caret_index_edit_order(); + + // Lists of carets representing each group + Vector<Vector<int>> caret_groups; + Vector<Pair<int, int>> group_borders; + + // Search for groups of carets and their selections residing on the same lines for (int i = 0; i < caret_edit_order.size(); i++) { int c = caret_edit_order[i]; - int cl = text_editor->get_caret_line(c); - bool swaped_caret = false; - for (int j = i + 1; j < caret_edit_order.size(); j++) { - if (text_editor->has_selection(caret_edit_order[j])) { - if (text_editor->get_selection_from_line() == cl) { - carets_to_remove.push_back(caret_edit_order[j]); - continue; - } + Vector<int> new_group{ c }; + Pair<int, int> group_border; + if (text_editor->has_selection(c)) { + group_border.first = text_editor->get_selection_from_line(c); + group_border.second = text_editor->get_selection_to_line(c); + } else { + group_border.first = text_editor->get_caret_line(c); + group_border.second = text_editor->get_caret_line(c); + } - if (text_editor->get_selection_to_line() == cl) { - if (text_editor->has_selection(c)) { - if (text_editor->get_selection_to_line(c) != cl) { - text_editor->select(cl + 1, 0, text_editor->get_selection_to_line(c), text_editor->get_selection_to_column(c), c); - break; - } - } + for (int j = i; j < caret_edit_order.size() - 1; j++) { + int c_current = caret_edit_order[j]; + int c_next = caret_edit_order[j + 1]; - carets_to_remove.push_back(c); - i = j - 1; - swaped_caret = true; - break; - } + int next_start_pos = text_editor->has_selection(c_next) ? text_editor->get_selection_from_line(c_next) : text_editor->get_caret_line(c_next); + int next_end_pos = text_editor->has_selection(c_next) ? text_editor->get_selection_to_line(c_next) : text_editor->get_caret_line(c_next); + + int current_start_pos = text_editor->has_selection(c_current) ? text_editor->get_selection_from_line(c_current) : text_editor->get_caret_line(c_current); + + i = j; + if (next_end_pos != current_start_pos && next_end_pos + 1 != current_start_pos) { break; } - - if (text_editor->get_caret_line(caret_edit_order[j]) == cl) { - carets_to_remove.push_back(caret_edit_order[j]); - i = j; - continue; + group_border.first = next_start_pos; + new_group.push_back(c_next); + // If the last caret is added to the current group there is no need to process it again + if (j + 1 == caret_edit_order.size() - 1) { + i++; } - break; } + group_borders.push_back(group_border); + caret_groups.push_back(new_group); + } - if (swaped_caret) { + for (int i = group_borders.size() - 1; i >= 0; i--) { + if (group_borders[i].first - 1 < 0) { continue; } - if (text_editor->has_selection(c)) { + // If the group starts overlapping with the upper group don't move it + if (i < group_borders.size() - 1 && group_borders[i].first - 1 <= group_borders[i + 1].second) { + continue; + } + + // We have to remember caret positions and selections prior to line swapping + Vector<Vector<int>> caret_group_parameters; + + for (int j = 0; j < caret_groups[i].size(); j++) { + int c = caret_groups[i][j]; + int cursor_line = text_editor->get_caret_line(c); + int cursor_column = text_editor->get_caret_column(c); + + if (!text_editor->has_selection(c)) { + caret_group_parameters.push_back(Vector<int>{ -1, -1, -1, -1, cursor_line, cursor_column }); + continue; + } int from_line = text_editor->get_selection_from_line(c); int from_col = text_editor->get_selection_from_column(c); int to_line = text_editor->get_selection_to_line(c); int to_column = text_editor->get_selection_to_column(c); - int cursor_line = text_editor->get_caret_line(c); + caret_group_parameters.push_back(Vector<int>{ from_line, from_col, to_line, to_column, cursor_line, cursor_column }); + } - for (int j = from_line; j <= to_line; j++) { - int line_id = j; - int next_id = j - 1; + for (int line_id = group_borders[i].first; line_id <= group_borders[i].second; line_id++) { + text_editor->unfold_line(line_id); + text_editor->unfold_line(line_id - 1); - if (line_id == 0 || next_id < 0) { - return; - } + text_editor->swap_lines(line_id - 1, line_id); + } - text_editor->unfold_line(line_id); - text_editor->unfold_line(next_id); + for (int j = 0; j < caret_groups[i].size(); j++) { + int c = caret_groups[i][j]; + Vector<int> caret_parameters = caret_group_parameters[j]; + text_editor->set_caret_line(caret_parameters[4] - 1, c == 0, true, 0, c); + text_editor->set_caret_column(caret_parameters[5], c == 0, c); - text_editor->swap_lines(line_id, next_id); - text_editor->set_caret_line(next_id, c == 0, true, 0, c); + if (caret_parameters[0] >= 0) { + text_editor->select(caret_parameters[0] - 1, caret_parameters[1], caret_parameters[2] - 1, caret_parameters[3], c); } - int from_line_up = from_line > 0 ? from_line - 1 : from_line; - int to_line_up = to_line > 0 ? to_line - 1 : to_line; - int cursor_line_up = cursor_line > 0 ? cursor_line - 1 : cursor_line; - text_editor->select(from_line_up, from_col, to_line_up, to_column, c); - text_editor->set_caret_line(cursor_line_up, c == 0, true, 0, c); - } else { - int line_id = text_editor->get_caret_line(c); - int next_id = line_id - 1; - - if (line_id == 0 || next_id < 0) { - return; - } - - text_editor->unfold_line(line_id); - text_editor->unfold_line(next_id); - - text_editor->swap_lines(line_id, next_id); - text_editor->set_caret_line(next_id, c == 0, true, 0, c); } } + text_editor->end_complex_operation(); text_editor->merge_overlapping_carets(); text_editor->queue_redraw(); @@ -1382,95 +1396,97 @@ void CodeTextEditor::move_lines_up() { void CodeTextEditor::move_lines_down() { text_editor->begin_complex_operation(); - Vector<int> carets_to_remove; - Vector<int> caret_edit_order = text_editor->get_caret_index_edit_order(); + + // Lists of carets representing each group + Vector<Vector<int>> caret_groups; + Vector<Pair<int, int>> group_borders; + + // Search for groups of carets and their selections residing on the same lines for (int i = 0; i < caret_edit_order.size(); i++) { int c = caret_edit_order[i]; - int cl = text_editor->get_caret_line(c); - bool swaped_caret = false; - for (int j = i + 1; j < caret_edit_order.size(); j++) { - if (text_editor->has_selection(caret_edit_order[j])) { - if (text_editor->get_selection_from_line() == cl) { - carets_to_remove.push_back(caret_edit_order[j]); - continue; - } + Vector<int> new_group{ c }; + Pair<int, int> group_border; + if (text_editor->has_selection(c)) { + group_border.first = text_editor->get_selection_from_line(c); + group_border.second = text_editor->get_selection_to_line(c); + } else { + group_border.first = text_editor->get_caret_line(c); + group_border.second = text_editor->get_caret_line(c); + } - if (text_editor->get_selection_to_line() == cl) { - if (text_editor->has_selection(c)) { - if (text_editor->get_selection_to_line(c) != cl) { - text_editor->select(cl + 1, 0, text_editor->get_selection_to_line(c), text_editor->get_selection_to_column(c), c); - break; - } - } + for (int j = i; j < caret_edit_order.size() - 1; j++) { + int c_current = caret_edit_order[j]; + int c_next = caret_edit_order[j + 1]; - carets_to_remove.push_back(c); - i = j - 1; - swaped_caret = true; - break; + int next_start_pos = text_editor->has_selection(c_next) ? text_editor->get_selection_from_line(c_next) : text_editor->get_caret_line(c_next); + int next_end_pos = text_editor->has_selection(c_next) ? text_editor->get_selection_to_line(c_next) : text_editor->get_caret_line(c_next); + + int current_start_pos = text_editor->has_selection(c_current) ? text_editor->get_selection_from_line(c_current) : text_editor->get_caret_line(c_current); + + i = j; + if (next_end_pos == current_start_pos || next_end_pos + 1 == current_start_pos) { + group_border.first = next_start_pos; + new_group.push_back(c_next); + // If the last caret is added to the current group there is no need to process it again + if (j + 1 == caret_edit_order.size() - 1) { + i++; } + } else { break; } - - if (text_editor->get_caret_line(caret_edit_order[j]) == cl) { - carets_to_remove.push_back(caret_edit_order[j]); - i = j; - continue; - } - break; } + group_borders.push_back(group_border); + caret_groups.push_back(new_group); + } - if (swaped_caret) { + for (int i = 0; i < group_borders.size(); i++) { + if (group_borders[i].second + 1 > text_editor->get_line_count() - 1) { continue; } - if (text_editor->has_selection(c)) { - int from_line = text_editor->get_selection_from_line(c); - int from_col = text_editor->get_selection_from_column(c); - int to_line = text_editor->get_selection_to_line(c); - int to_column = text_editor->get_selection_to_column(c); - int cursor_line = text_editor->get_caret_line(c); - - for (int l = to_line; l >= from_line; l--) { - int line_id = l; - int next_id = l + 1; - - if (line_id == text_editor->get_line_count() - 1 || next_id > text_editor->get_line_count()) { - continue; - } - - text_editor->unfold_line(line_id); - text_editor->unfold_line(next_id); + // If the group starts overlapping with the upper group don't move it + if (i > 0 && group_borders[i].second + 1 >= group_borders[i - 1].first) { + continue; + } - text_editor->swap_lines(line_id, next_id); - text_editor->set_caret_line(next_id, c == 0, true, 0, c); - } - int from_line_down = from_line < text_editor->get_line_count() ? from_line + 1 : from_line; - int to_line_down = to_line < text_editor->get_line_count() ? to_line + 1 : to_line; - int cursor_line_down = cursor_line < text_editor->get_line_count() ? cursor_line + 1 : cursor_line; - text_editor->select(from_line_down, from_col, to_line_down, to_column, c); - text_editor->set_caret_line(cursor_line_down, c == 0, true, 0, c); - } else { - int line_id = text_editor->get_caret_line(c); - int next_id = line_id + 1; + // We have to remember caret positions and selections prior to line swapping + Vector<Vector<int>> caret_group_parameters; - if (line_id == text_editor->get_line_count() - 1 || next_id > text_editor->get_line_count()) { - continue; + for (int j = 0; j < caret_groups[i].size(); j++) { + int c = caret_groups[i][j]; + int cursor_line = text_editor->get_caret_line(c); + int cursor_column = text_editor->get_caret_column(c); + + if (text_editor->has_selection(c)) { + int from_line = text_editor->get_selection_from_line(c); + int from_col = text_editor->get_selection_from_column(c); + int to_line = text_editor->get_selection_to_line(c); + int to_column = text_editor->get_selection_to_column(c); + caret_group_parameters.push_back(Vector<int>{ from_line, from_col, to_line, to_column, cursor_line, cursor_column }); + } else { + caret_group_parameters.push_back(Vector<int>{ -1, -1, -1, -1, cursor_line, cursor_column }); } + } + for (int line_id = group_borders[i].second; line_id >= group_borders[i].first; line_id--) { text_editor->unfold_line(line_id); - text_editor->unfold_line(next_id); + text_editor->unfold_line(line_id + 1); - text_editor->swap_lines(line_id, next_id); - text_editor->set_caret_line(next_id, c == 0, true, 0, c); + text_editor->swap_lines(line_id + 1, line_id); } - } - // Sort and remove backwards to preserve indexes. - carets_to_remove.sort(); - for (int i = carets_to_remove.size() - 1; i >= 0; i--) { - text_editor->remove_caret(carets_to_remove[i]); + for (int j = 0; j < caret_groups[i].size(); j++) { + int c = caret_groups[i][j]; + Vector<int> caret_parameters = caret_group_parameters[j]; + text_editor->set_caret_line(caret_parameters[4] + 1, c == 0, true, 0, c); + text_editor->set_caret_column(caret_parameters[5], c == 0, c); + + if (caret_parameters[0] >= 0) { + text_editor->select(caret_parameters[0] + 1, caret_parameters[1], caret_parameters[2] + 1, caret_parameters[3], c); + } + } } text_editor->merge_overlapping_carets(); diff --git a/editor/code_editor.h b/editor/code_editor.h index ded7518287..4d7034fbd0 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -158,6 +158,7 @@ class CodeTextEditor : public VBoxContainer { Label *info = nullptr; Timer *idle = nullptr; Timer *code_complete_timer = nullptr; + int code_complete_timer_line = 0; Timer *font_resize_timer = nullptr; int font_resize_val; diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 785476d75b..2adab089e4 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -275,7 +275,8 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String r_item->set_text(0, "\"" + p_type + "\""); } else if (script_type) { r_item->set_metadata(0, p_type); - r_item->set_text(0, p_type + " (" + ScriptServer::get_global_class_path(p_type).get_file() + ")"); + r_item->set_text(0, p_type); + r_item->set_suffix(0, "(" + ScriptServer::get_global_class_path(p_type).get_file() + ")"); } else { r_item->set_metadata(0, custom_type_parents[p_type]); r_item->set_text(0, p_type); diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp index b92b0fca59..609afeb40a 100644 --- a/editor/editor_command_palette.cpp +++ b/editor/editor_command_palette.cpp @@ -38,6 +38,9 @@ EditorCommandPalette *EditorCommandPalette::singleton = nullptr; +static Rect2i prev_rect = Rect2i(); +static bool was_showed = false; + float EditorCommandPalette::_score_path(const String &p_search, const String &p_path) { float score = 0.9f + .1f * (p_search.length() / (float)p_path.length()); @@ -145,6 +148,17 @@ void EditorCommandPalette::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_command", "key_name"), &EditorCommandPalette::remove_command); } +void EditorCommandPalette::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible()) { + prev_rect = Rect2i(get_position(), get_size()); + was_showed = true; + } + } break; + } +} + void EditorCommandPalette::_sbox_input(const Ref<InputEvent> &p_ie) { Ref<InputEventKey> k = p_ie; if (k.is_valid()) { @@ -171,12 +185,10 @@ void EditorCommandPalette::_confirmed() { } void EditorCommandPalette::open_popup() { - static bool was_showed = false; - if (!was_showed) { - was_showed = true; - popup_centered_clamped(Size2(600, 440) * EDSCALE, 0.8f); + if (was_showed) { + popup(prev_rect); } else { - show(); + popup_centered_clamped(Size2(600, 440) * EDSCALE, 0.8f); } command_search_box->clear(); diff --git a/editor/editor_command_palette.h b/editor/editor_command_palette.h index 15200552b4..81cf401851 100644 --- a/editor/editor_command_palette.h +++ b/editor/editor_command_palette.h @@ -91,6 +91,7 @@ class EditorCommandPalette : public ConfirmationDialog { protected: static void _bind_methods(); + void _notification(int p_what); public: void open_popup(); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 5d137ce290..678ec04b9d 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1642,6 +1642,7 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector String importer_name; HashMap<String, HashMap<StringName, Variant>> source_file_options; + HashMap<String, ResourceUID::ID> uids; HashMap<String, String> base_paths; for (int i = 0; i < p_files.size(); i++) { Ref<ConfigFile> config; @@ -1657,6 +1658,15 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector ERR_FAIL_V(ERR_FILE_CORRUPT); } + ResourceUID::ID uid = ResourceUID::INVALID_ID; + + if (config->has_section_key("remap", "uid")) { + String uidt = config->get_value("remap", "uid"); + uid = ResourceUID::get_singleton()->text_to_id(uidt); + } + + uids[p_files[i]] = uid; + source_file_options[p_files[i]] = HashMap<StringName, Variant>(); importer_name = file_importer_name; @@ -1701,6 +1711,7 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector const String &file = E.key; String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(file); Vector<String> dest_paths; + ResourceUID::ID uid = uids[file]; { Ref<FileAccess> f = FileAccess::open(file + ".import", FileAccess::WRITE); ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, "Cannot open import file '" + file + ".import'."); @@ -1717,6 +1728,12 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector f->store_line("type=\"" + importer->get_resource_type() + "\""); } + if (uid == ResourceUID::INVALID_ID) { + uid = ResourceUID::get_singleton()->create_id(); + } + + f->store_line("uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\""); // Store in readable format. + if (err == OK) { String path = base_path + "." + importer->get_save_extension(); f->store_line("path=\"" + path + "\""); @@ -1782,12 +1799,19 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector fs->files[cpos]->modified_time = FileAccess::get_modified_time(file); fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(file + ".import"); fs->files[cpos]->deps = _get_dependencies(file); + fs->files[cpos]->uid = uid; fs->files[cpos]->type = importer->get_resource_type(); if (fs->files[cpos]->type == "" && textfile_extensions.has(file.get_extension())) { fs->files[cpos]->type = "TextFile"; } fs->files[cpos]->import_valid = err == OK; + if (ResourceUID::get_singleton()->has_id(uid)) { + ResourceUID::get_singleton()->set_id(uid, file); + } else { + ResourceUID::get_singleton()->add_id(uid, file); + } + //if file is currently up, maybe the source it was loaded from changed, so import math must be updated for it //to reload properly Ref<Resource> r = ResourceCache::get_ref(file); @@ -2074,6 +2098,8 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) { importing = true; EditorProgress pr("reimport", TTR("(Re)Importing Assets"), p_files.size()); + Vector<String> reloads; + Vector<ImportFile> reimport_files; HashSet<String> groups_to_reimport; @@ -2089,22 +2115,26 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) { String group_file = ResourceFormatImporter::get_singleton()->get_import_group_file(file); if (group_file_cache.has(file)) { - //maybe the file itself is a group! + // Maybe the file itself is a group! groups_to_reimport.insert(file); - //groups do not belong to groups + // Groups do not belong to groups. + group_file = String(); + } else if (groups_to_reimport.has(file)) { + // Groups do not belong to groups. group_file = String(); } else if (!group_file.is_empty()) { - //it's a group file, add group to import and skip this file + // It's a group file, add group to import and skip this file. groups_to_reimport.insert(group_file); } else { - //it's a regular file + // It's a regular file. ImportFile ifile; ifile.path = file; ResourceFormatImporter::get_singleton()->get_import_order_threads_and_importer(file, ifile.order, ifile.threaded, ifile.importer); + reloads.push_back(file); reimport_files.push_back(ifile); } - //group may have changed, so also update group reference + // Group may have changed, so also update group reference. EditorFileSystemDirectory *fs = nullptr; int cpos = -1; if (_find_file(file, &fs, cpos)) { @@ -2118,10 +2148,14 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) { int from = 0; for (int i = 0; i < reimport_files.size(); i++) { + if (groups_to_reimport.has(reimport_files[i].path)) { + continue; + } + if (use_multiple_threads && reimport_files[i].threaded) { if (i + 1 == reimport_files.size() || reimport_files[i + 1].importer != reimport_files[from].importer) { if (from - i == 0) { - //single file, do not use threads + // Single file, do not use threads. pr.step(reimport_files[i].path.get_file(), i); _reimport_file(reimport_files[i].path); } else { @@ -2159,20 +2193,25 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) { } } - //reimport groups + // Reimport groups. + + from = reimport_files.size(); if (groups_to_reimport.size()) { HashMap<String, Vector<String>> group_files; _find_group_files(filesystem, group_files, groups_to_reimport); for (const KeyValue<String, Vector<String>> &E : group_files) { + pr.step(E.key.get_file(), from++); Error err = _reimport_group(E.key, E.value); + reloads.push_back(E.key); + reloads.append_array(E.value); if (err == OK) { _reimport_file(E.key); } } } - ResourceUID::get_singleton()->update_cache(); //after reimporting, update the cache + ResourceUID::get_singleton()->update_cache(); // After reimporting, update the cache. _save_filesystem_cache(); importing = false; @@ -2180,7 +2219,7 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) { emit_signal(SNAME("filesystem_changed")); } - emit_signal(SNAME("resources_reimported"), p_files); + emit_signal(SNAME("resources_reimported"), reloads); } Error EditorFileSystem::_resource_import(const String &p_path) { diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index e7d4636ad9..cf413133aa 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -41,7 +41,7 @@ Ref<FontFile> load_external_font(const String &p_path, TextServer::Hinting p_hin Ref<FontFile> font; font.instantiate(); - Vector<uint8_t> data = FileAccess::get_file_as_array(p_path); + Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_path); font->set_data(data); font->set_multichannel_signed_distance_field(p_msdf); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index d00a64f796..463e8f6bdc 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2274,7 +2274,9 @@ void EditorNode::_edit_current(bool p_skip_foreign) { } else { Node *selected_node = nullptr; - if (current_obj->is_class("MultiNodeEdit")) { + if (current_obj->is_class("EditorDebuggerRemoteObject")) { + disable_folding = true; + } else if (current_obj->is_class("MultiNodeEdit")) { Node *scene = get_edited_scene(); if (scene) { MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(current_obj); @@ -6036,14 +6038,7 @@ EditorNode::EditorNode() { Input *id = Input::get_singleton(); if (id) { - bool found_touchscreen = false; - for (int i = 0; i < DisplayServer::get_singleton()->get_screen_count(); i++) { - if (DisplayServer::get_singleton()->screen_is_touchscreen(i)) { - found_touchscreen = true; - } - } - - if (!found_touchscreen && Input::get_singleton()) { + if (!DisplayServer::get_singleton()->is_touchscreen_available() && Input::get_singleton()) { // Only if no touchscreen ui hint, disable emulation just in case. id->set_emulate_touch_from_mouse(false); } diff --git a/editor/editor_quick_open.cpp b/editor/editor_quick_open.cpp index bb533b88d6..6ddccba0e2 100644 --- a/editor/editor_quick_open.cpp +++ b/editor/editor_quick_open.cpp @@ -34,23 +34,24 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" -void EditorQuickOpen::popup_dialog(const String &p_base, bool p_enable_multi, bool p_dontclear) { +static Rect2i prev_rect = Rect2i(); +static bool was_showed = false; + +void EditorQuickOpen::popup_dialog(const String &p_base, bool p_enable_multi, bool p_dont_clear) { base_type = p_base; allow_multi_select = p_enable_multi; search_options->set_select_mode(allow_multi_select ? Tree::SELECT_MULTI : Tree::SELECT_SINGLE); - static bool was_showed = false; - if (!was_showed) { - was_showed = true; - popup_centered_clamped(Size2(600, 440) * EDSCALE, 0.8f); + if (was_showed) { + popup(prev_rect); } else { - show(); + popup_centered_clamped(Size2(600, 440) * EDSCALE, 0.8f); } EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->get_filesystem(); _build_search_cache(efsd); - if (p_dontclear) { + if (p_dont_clear) { search_box->select_all(); _update_search(); } else { @@ -251,6 +252,13 @@ void EditorQuickOpen::_notification(int p_what) { search_box->set_clear_button_enabled(true); } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible()) { + prev_rect = Rect2i(get_position(), get_size()); + was_showed = true; + } + } break; + case NOTIFICATION_EXIT_TREE: { disconnect("confirmed", callable_mp(this, &EditorQuickOpen::_confirmed)); } break; diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 11a8fce9c3..9128143619 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -582,6 +582,7 @@ void EditorSpinSlider::_value_focus_exited() { //tab was pressed } else { //enter, click, esc + grab_focus(); } } diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 7c5c7da2ef..c01db215da 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -442,10 +442,11 @@ HashSet<String> EditorExportPlatform::get_features(const Ref<EditorExportPreset> result.insert(E); } + result.insert("template"); if (p_debug) { - result.insert("debug"); + result.insert("template_debug"); } else { - result.insert("release"); + result.insert("template_release"); } if (!p_preset->get_custom_features().is_empty()) { @@ -1034,7 +1035,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & return err; } // Now actual remapped file: - sarr = FileAccess::get_file_as_array(export_path); + sarr = FileAccess::get_file_as_bytes(export_path); err = p_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; @@ -1053,7 +1054,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & if (importer_type == "keep") { //just keep file as-is - Vector<uint8_t> array = FileAccess::get_file_as_array(path); + Vector<uint8_t> array = FileAccess::get_file_as_bytes(path); err = p_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { @@ -1086,14 +1087,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & String remap = F; if (remap == "path") { String remapped_path = config->get_value("remap", remap); - Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path); + Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path); err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); } else if (remap.begins_with("path.")) { String feature = remap.get_slice(".", 1); if (remap_features.has(feature)) { String remapped_path = config->get_value("remap", remap); - Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path); + Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path); err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); } } @@ -1104,7 +1105,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } //also save the .import file - Vector<uint8_t> array = FileAccess::get_file_as_array(path + ".import"); + Vector<uint8_t> array = FileAccess::get_file_as_bytes(path + ".import"); err = p_func(p_udata, path + ".import", array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { @@ -1164,7 +1165,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & path_remaps.push_back(export_path); } - Vector<uint8_t> array = FileAccess::get_file_as_array(export_path); + Vector<uint8_t> array = FileAccess::get_file_as_bytes(export_path); err = p_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; @@ -1244,14 +1245,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & String icon = GLOBAL_GET("application/config/icon"); String splash = GLOBAL_GET("application/boot_splash/image"); if (!icon.is_empty() && FileAccess::exists(icon)) { - Vector<uint8_t> array = FileAccess::get_file_as_array(icon); + Vector<uint8_t> array = FileAccess::get_file_as_bytes(icon); err = p_func(p_udata, icon, array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; } } if (!splash.is_empty() && FileAccess::exists(splash) && icon != splash) { - Vector<uint8_t> array = FileAccess::get_file_as_array(splash); + Vector<uint8_t> array = FileAccess::get_file_as_bytes(splash); err = p_func(p_udata, splash, array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; @@ -1259,7 +1260,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } String resource_cache_file = ResourceUID::get_cache_file(); if (FileAccess::exists(resource_cache_file)) { - Vector<uint8_t> array = FileAccess::get_file_as_array(resource_cache_file); + Vector<uint8_t> array = FileAccess::get_file_as_bytes(resource_cache_file); err = p_func(p_udata, resource_cache_file, array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; @@ -1268,7 +1269,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & String extension_list_config_file = NativeExtension::get_extension_list_config_file(); if (FileAccess::exists(extension_list_config_file)) { - Vector<uint8_t> array = FileAccess::get_file_as_array(extension_list_config_file); + Vector<uint8_t> array = FileAccess::get_file_as_bytes(extension_list_config_file); err = p_func(p_udata, extension_list_config_file, array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; @@ -1282,7 +1283,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & // Try using user provided data file. String ts_data = "res://" + TS->get_support_data_filename(); if (FileAccess::exists(ts_data)) { - Vector<uint8_t> array = FileAccess::get_file_as_array(ts_data); + Vector<uint8_t> array = FileAccess::get_file_as_bytes(ts_data); err = p_func(p_udata, ts_data, array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; @@ -1291,7 +1292,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & // Use default text server data. String icu_data_file = EditorPaths::get_singleton()->get_cache_dir().path_join("tmp_icu_data"); TS->save_support_data(icu_data_file); - Vector<uint8_t> array = FileAccess::get_file_as_array(icu_data_file); + Vector<uint8_t> array = FileAccess::get_file_as_bytes(icu_data_file); err = p_func(p_udata, ts_data, array, idx, total, enc_in_filters, enc_ex_filters, key); DirAccess::remove_file_or_error(icu_data_file); if (err != OK) { @@ -1304,7 +1305,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & String config_file = "project.binary"; String engine_cfb = EditorPaths::get_singleton()->get_cache_dir().path_join("tmp" + config_file); ProjectSettings::get_singleton()->save_custom(engine_cfb, custom_map, custom_list); - Vector<uint8_t> data = FileAccess::get_file_as_array(engine_cfb); + Vector<uint8_t> data = FileAccess::get_file_as_bytes(engine_cfb); DirAccess::remove_file_or_error(engine_cfb); return p_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key); diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp index 9de2f94900..5345346c48 100644 --- a/editor/export/editor_export_platform_pc.cpp +++ b/editor/export/editor_export_platform_pc.cpp @@ -81,8 +81,8 @@ bool EditorExportPlatformPC::has_valid_export_configuration(const Ref<EditorExpo // Look for export templates (first official, and if defined custom templates). String arch = p_preset->get("binary_format/architecture"); - bool dvalid = exists_export_template(get_template_file_name("debug", arch), &err); - bool rvalid = exists_export_template(get_template_file_name("release", arch), &err); + bool dvalid = exists_export_template(get_template_file_name("template_debug", arch), &err); + bool rvalid = exists_export_template(get_template_file_name("template_release", arch), &err); if (p_preset->get("custom_template/debug") != "") { dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp index 78d4f93dfe..f0e841f307 100644 --- a/editor/export/editor_export_plugin.cpp +++ b/editor/export/editor_export_plugin.cpp @@ -148,10 +148,8 @@ bool EditorExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatfo Ref<Resource> EditorExportPlugin::_customize_resource(const Ref<Resource> &p_resource, const String &p_path) { Ref<Resource> ret; - if (GDVIRTUAL_REQUIRED_CALL(_customize_resource, p_resource, p_path, ret)) { - return ret; - } - return Ref<Resource>(); + GDVIRTUAL_REQUIRED_CALL(_customize_resource, p_resource, p_path, ret); + return ret; } bool EditorExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const { @@ -162,18 +160,14 @@ bool EditorExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> Node *EditorExportPlugin::_customize_scene(Node *p_root, const String &p_path) { Node *ret = nullptr; - if (GDVIRTUAL_REQUIRED_CALL(_customize_scene, p_root, p_path, ret)) { - return ret; - } - return nullptr; + GDVIRTUAL_REQUIRED_CALL(_customize_scene, p_root, p_path, ret); + return ret; } uint64_t EditorExportPlugin::_get_customization_configuration_hash() const { uint64_t ret = 0; - if (GDVIRTUAL_REQUIRED_CALL(_get_customization_configuration_hash, ret)) { - return ret; - } - return 0; + GDVIRTUAL_REQUIRED_CALL(_get_customization_configuration_hash, ret); + return ret; } void EditorExportPlugin::_end_customize_scenes() { @@ -186,10 +180,8 @@ void EditorExportPlugin::_end_customize_resources() { String EditorExportPlugin::_get_name() const { String ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_name, ret)) { - return ret; - } - return ""; + GDVIRTUAL_REQUIRED_CALL(_get_name, ret); + return ret; } void EditorExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) { diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 8df5808b11..6316a7f545 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1414,7 +1414,7 @@ void FileSystemDock::_update_dependencies_after_move(const HashMap<String, Strin Error err = ResourceLoader::rename_dependencies(file, p_renames); if (err == OK) { if (ResourceLoader::get_resource_type(file) == "PackedScene") { - EditorNode::get_singleton()->reload_scene(file); + callable_mp(EditorNode::get_singleton(), &EditorNode::reload_scene).bind(file).call_deferred(); } } else { EditorNode::get_singleton()->add_io_error(TTR("Unable to update dependencies:") + "\n" + remaps[i] + "\n"); diff --git a/editor/icons/TrackTrigger.svg b/editor/icons/TrackTrigger.svg deleted file mode 100644 index c403fba59a..0000000000 --- a/editor/icons/TrackTrigger.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v2h2v4h2v-4h2v-2zm13 0a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-3 2a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-3 2a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1z" fill="#f68f45"/></svg> diff --git a/editor/import/dynamic_font_import_settings.cpp b/editor/import/dynamic_font_import_settings.cpp index 0aa77f6ea0..75d0abbb0c 100644 --- a/editor/import/dynamic_font_import_settings.cpp +++ b/editor/import/dynamic_font_import_settings.cpp @@ -469,6 +469,8 @@ void DynamicFontImportSettings::_main_prop_changed(const String &p_edited_proper font_preview->set_msdf_pixel_range(import_settings_data->get("msdf_pixel_range")); } else if (p_edited_property == "msdf_size") { font_preview->set_msdf_size(import_settings_data->get("msdf_size")); + } else if (p_edited_property == "allow_system_fallback") { + font_preview->set_allow_system_fallback(import_settings_data->get("allow_system_fallback")); } else if (p_edited_property == "force_autohinter") { font_preview->set_force_autohinter(import_settings_data->get("force_autohinter")); } else if (p_edited_property == "hinting") { @@ -936,6 +938,7 @@ void DynamicFontImportSettings::_re_import() { main_settings["multichannel_signed_distance_field"] = import_settings_data->get("multichannel_signed_distance_field"); main_settings["msdf_pixel_range"] = import_settings_data->get("msdf_pixel_range"); main_settings["msdf_size"] = import_settings_data->get("msdf_size"); + main_settings["allow_system_fallback"] = import_settings_data->get("allow_system_fallback"); main_settings["force_autohinter"] = import_settings_data->get("force_autohinter"); main_settings["hinting"] = import_settings_data->get("hinting"); main_settings["subpixel_positioning"] = import_settings_data->get("subpixel_positioning"); @@ -1036,7 +1039,7 @@ void DynamicFontImportSettings::_process_locales() { void DynamicFontImportSettings::open_settings(const String &p_path) { // Load base font data. - Vector<uint8_t> font_data = FileAccess::get_file_as_array(p_path); + Vector<uint8_t> font_data = FileAccess::get_file_as_bytes(p_path); // Load project locale list. locale_tree->clear(); @@ -1202,6 +1205,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) { font_preview->set_multichannel_signed_distance_field(import_settings_data->get("multichannel_signed_distance_field")); font_preview->set_msdf_pixel_range(import_settings_data->get("msdf_pixel_range")); font_preview->set_msdf_size(import_settings_data->get("msdf_size")); + font_preview->set_allow_system_fallback(import_settings_data->get("allow_system_fallback")); font_preview->set_force_autohinter(import_settings_data->get("force_autohinter")); font_preview->set_hinting((TextServer::Hinting)import_settings_data->get("hinting").operator int()); font_preview->set_subpixel_positioning((TextServer::SubpixelPositioning)import_settings_data->get("subpixel_positioning").operator int()); @@ -1232,6 +1236,7 @@ DynamicFontImportSettings::DynamicFontImportSettings() { options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true)); options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), 8)); options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_RANGE, "1,250,1"), 48)); + options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "allow_system_fallback"), true)); options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false)); options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1)); options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), 1)); diff --git a/editor/import/resource_importer_bmfont.cpp b/editor/import/resource_importer_bmfont.cpp index 4fb1b726bd..357faf0022 100644 --- a/editor/import/resource_importer_bmfont.cpp +++ b/editor/import/resource_importer_bmfont.cpp @@ -76,6 +76,7 @@ Error ResourceImporterBMFont::import(const String &p_source_file, const String & Error err = font->load_bitmap_font(p_source_file); ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load font to file \"" + p_source_file + "\"."); + font->set_allow_system_fallback(false); font->set_fallbacks(fallbacks); int flg = 0; diff --git a/editor/import/resource_importer_dynamic_font.cpp b/editor/import/resource_importer_dynamic_font.cpp index 44440a92bd..2f1445af48 100644 --- a/editor/import/resource_importer_dynamic_font.cpp +++ b/editor/import/resource_importer_dynamic_font.cpp @@ -114,6 +114,7 @@ void ResourceImporterDynamicFont::get_import_options(const String &p_path, List< r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), 8)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_RANGE, "1,250,1"), 48)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "allow_system_fallback"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), 1)); @@ -150,13 +151,14 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str Dictionary ot_ov = p_options["opentype_features"]; bool autohinter = p_options["force_autohinter"]; + bool allow_system_fallback = p_options["allow_system_fallback"]; int hinting = p_options["hinting"]; int subpixel_positioning = p_options["subpixel_positioning"]; real_t oversampling = p_options["oversampling"]; Array fallbacks = p_options["fallbacks"]; // Load base font data. - Vector<uint8_t> data = FileAccess::get_file_as_array(p_source_file); + Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_source_file); // Create font. Ref<FontFile> font; @@ -170,6 +172,7 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str font->set_opentype_feature_overrides(ot_ov); font->set_fixed_size(0); font->set_force_autohinter(autohinter); + font->set_allow_system_fallback(allow_system_fallback); font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning); font->set_hinting((TextServer::Hinting)hinting); font->set_oversampling(oversampling); diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp index eb4916663e..9c3304d946 100644 --- a/editor/import/resource_importer_imagefont.cpp +++ b/editor/import/resource_importer_imagefont.cpp @@ -121,6 +121,7 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin font->set_fixed_size(chr_height); font->set_subpixel_positioning(TextServer::SUBPIXEL_POSITIONING_DISABLED); font->set_force_autohinter(false); + font->set_allow_system_fallback(false); font->set_hinting(TextServer::HINTING_NONE); font->set_oversampling(1.0f); font->set_fallbacks(fallbacks); diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index a9ce6e9f88..9566c1b23c 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -2448,7 +2448,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p Vector<Vector<uint8_t>> mesh_lightmap_caches; { - src_lightmap_cache = FileAccess::get_file_as_array(p_source_file + ".unwrap_cache", &err); + src_lightmap_cache = FileAccess::get_file_as_bytes(p_source_file + ".unwrap_cache", &err); if (err != OK) { src_lightmap_cache.clear(); } diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 0f67663948..dbd1b12a94 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -376,11 +376,11 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { undo_redo->commit_action(); } -void AnimationNodeBlendTreeEditor::_popup(bool p_has_input_ports, const Vector2 &p_popup_position, const Vector2 &p_node_position) { +void AnimationNodeBlendTreeEditor::_popup(bool p_has_input_ports, const Vector2 &p_node_position) { _update_options_menu(p_has_input_ports); use_position_from_popup_menu = true; position_from_popup_menu = p_node_position; - add_node->get_popup()->set_position(p_popup_position); + add_node->get_popup()->set_position(graph->get_screen_position() + graph->get_local_mouse_position()); add_node->get_popup()->reset_size(); add_node->get_popup()->popup(); } @@ -390,7 +390,7 @@ void AnimationNodeBlendTreeEditor::_popup_request(const Vector2 &p_position) { return; } - _popup(false, graph->get_screen_position() + graph->get_local_mouse_position(), p_position); + _popup(false, p_position); } void AnimationNodeBlendTreeEditor::_connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position) { @@ -401,7 +401,7 @@ void AnimationNodeBlendTreeEditor::_connection_to_empty(const String &p_from, in Ref<AnimationNode> node = blend_tree->get_node(p_from); if (node.is_valid()) { from_node = p_from; - _popup(true, p_release_position, graph->get_global_mouse_position()); + _popup(true, p_release_position); } } @@ -414,7 +414,7 @@ void AnimationNodeBlendTreeEditor::_connection_from_empty(const String &p_to, in if (node.is_valid()) { to_node = p_to; to_slot = p_to_slot; - _popup(false, p_release_position, graph->get_global_mouse_position()); + _popup(false, p_release_position); } } diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index fb19cce147..4b55aa9b3f 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -117,7 +117,7 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _filter_toggled(); Ref<AnimationNode> _filter_edit; - void _popup(bool p_has_input_ports, const Vector2 &p_popup_position, const Vector2 &p_node_position); + void _popup(bool p_has_input_ports, const Vector2 &p_node_position); void _popup_request(const Vector2 &p_position); void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position); void _connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index f8ebd377d1..5183a738ae 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -199,6 +199,7 @@ void AnimationPlayerEditor::_play_pressed() { if (current == player->get_assigned_animation()) { player->stop(); //so it won't blend with itself } + ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing."); player->play(current); } @@ -211,13 +212,12 @@ void AnimationPlayerEditor::_play_from_pressed() { if (!current.is_empty()) { float time = player->get_current_animation_position(); - if (current == player->get_assigned_animation() && player->is_playing()) { player->stop(); //so it won't blend with itself } - - player->play(current); + ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing."); player->seek(time); + player->play(current); } //unstop @@ -237,6 +237,7 @@ void AnimationPlayerEditor::_play_bw_pressed() { if (current == player->get_assigned_animation()) { player->stop(); //so it won't blend with itself } + ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing."); player->play(current, -1, -1, true); } @@ -252,9 +253,9 @@ void AnimationPlayerEditor::_play_bw_from_pressed() { if (current == player->get_assigned_animation()) { player->stop(); //so it won't blend with itself } - - player->play(current, -1, -1, true); + ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing."); player->seek(time); + player->play(current, -1, -1, true); } //unstop @@ -1564,6 +1565,53 @@ void AnimationPlayerEditor::_pin_pressed() { SceneTreeDock::get_singleton()->get_tree_editor()->update_tree(); } +bool AnimationPlayerEditor::_validate_tracks(const Ref<Animation> p_anim) { + bool is_valid = true; + if (!p_anim.is_valid()) { + return true; // There is a problem outside of the animation track. + } + int len = p_anim->get_track_count(); + for (int i = 0; i < len; i++) { + Animation::TrackType ttype = p_anim->track_get_type(i); + if (ttype == Animation::TYPE_ROTATION_3D) { + int key_len = p_anim->track_get_key_count(i); + for (int j = 0; j < key_len; j++) { + Quaternion q; + p_anim->rotation_track_get_key(i, j, &q); + ERR_BREAK_EDMSG(!q.is_normalized(), "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', rotation track: '" + p_anim->track_get_path(i) + "' contains unnormalized Quaternion key."); + } + } else if (ttype == Animation::TYPE_VALUE) { + int key_len = p_anim->track_get_key_count(i); + if (key_len == 0) { + continue; + } + switch (p_anim->track_get_key_value(i, 0).get_type()) { + case Variant::QUATERNION: { + for (int j = 0; j < key_len; j++) { + Quaternion q = Quaternion(p_anim->track_get_key_value(i, j)); + if (!q.is_normalized()) { + is_valid = false; + ERR_BREAK_EDMSG(true, "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', value track: '" + p_anim->track_get_path(i) + "' contains unnormalized Quaternion key."); + } + } + } break; + case Variant::TRANSFORM3D: { + for (int j = 0; j < key_len; j++) { + Transform3D t = Transform3D(p_anim->track_get_key_value(i, j)); + if (!t.basis.orthonormalized().is_rotation()) { + is_valid = false; + ERR_BREAK_EDMSG(true, "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', value track: '" + p_anim->track_get_path(i) + "' contains corrupted basis (some axes are too close other axis or scaled by zero) Transform3D key."); + } + } + } break; + default: { + } break; + } + } + } + return is_valid; +} + void AnimationPlayerEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_animation_new"), &AnimationPlayerEditor::_animation_new); ClassDB::bind_method(D_METHOD("_animation_rename"), &AnimationPlayerEditor::_animation_rename); diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 6370b00ea8..53d460fc9e 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -212,6 +212,8 @@ class AnimationPlayerEditor : public VBoxContainer { void _start_onion_skinning(); void _stop_onion_skinning(); + bool _validate_tracks(const Ref<Animation> p_anim); + void _pin_pressed(); String _get_current() const; diff --git a/editor/plugins/gdextension_export_plugin.h b/editor/plugins/gdextension_export_plugin.h index c5f7d2a047..586cd2bd59 100644 --- a/editor/plugins/gdextension_export_plugin.h +++ b/editor/plugins/gdextension_export_plugin.h @@ -54,60 +54,38 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p String entry_symbol = config->get_value("configuration", "entry_symbol"); - List<String> libraries; - - config->get_section_keys("libraries", &libraries); - - bool could_export = false; - for (const String &E : libraries) { - Vector<String> tags = E.split("."); - bool all_tags_met = true; - for (int i = 0; i < tags.size(); i++) { - String tag = tags[i].strip_edges(); - if (!p_features.has(tag)) { - all_tags_met = false; - break; - } - } - - if (all_tags_met) { - String library_path = config->get_value("libraries", E); - if (!library_path.begins_with("res://")) { - print_line("Skipping export of out-of-project library " + library_path); - continue; - } - add_shared_object(library_path, tags); - - if (p_features.has("iOS") && (library_path.ends_with(".a") || library_path.ends_with(".xcframework"))) { - String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n" - "extern void add_ios_init_callback(void (*cb)());\n" - "\n" - "extern \"C\" void $ENTRY();\n" - "void $ENTRY_init() {\n" - " if (&$ENTRY) register_dynamic_symbol((char *)\"$ENTRY\", (void *)$ENTRY);\n" - "}\n" - "struct $ENTRY_struct {\n" - " $ENTRY_struct() {\n" - " add_ios_init_callback($ENTRY_init);\n" - " }\n" - "};\n" - "$ENTRY_struct $ENTRY_struct_instance;\n\n"; - additional_code = additional_code.replace("$ENTRY", entry_symbol); - add_ios_cpp_code(additional_code); - - String linker_flags = "-Wl,-U,_" + entry_symbol; - add_ios_linker_flags(linker_flags); - } - could_export = true; - break; + PackedStringArray tags; + String library_path = NativeExtension::find_extension_library( + p_path, config, [p_features](String p_feature) { return p_features.has(p_feature); }, &tags); + if (!library_path.is_empty()) { + add_shared_object(library_path, tags); + + if (p_features.has("iOS") && (library_path.ends_with(".a") || library_path.ends_with(".xcframework"))) { + String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n" + "extern void add_ios_init_callback(void (*cb)());\n" + "\n" + "extern \"C\" void $ENTRY();\n" + "void $ENTRY_init() {\n" + " if (&$ENTRY) register_dynamic_symbol((char *)\"$ENTRY\", (void *)$ENTRY);\n" + "}\n" + "struct $ENTRY_struct {\n" + " $ENTRY_struct() {\n" + " add_ios_init_callback($ENTRY_init);\n" + " }\n" + "};\n" + "$ENTRY_struct $ENTRY_struct_instance;\n\n"; + additional_code = additional_code.replace("$ENTRY", entry_symbol); + add_ios_cpp_code(additional_code); + + String linker_flags = "-Wl,-U,_" + entry_symbol; + add_ios_linker_flags(linker_flags); } - } - if (!could_export) { - Vector<String> tags; + } else { + Vector<String> features_vector; for (const String &E : p_features) { - tags.append(E); + features_vector.append(E); } - ERR_FAIL_MSG(vformat("No suitable library found. The libraries' tags referred to an invalid feature flag. Possible feature flags for your platform: %s", p_path, String(", ").join(tags))); + ERR_FAIL_MSG(vformat("No suitable library found for GDExtension: %s. Possible feature flags for your platform: %s", p_path, String(", ").join(features_vector))); } List<String> dependencies; @@ -115,11 +93,11 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p config->get_section_keys("dependencies", &dependencies); } - for (const String &E : libraries) { - Vector<String> tags = E.split("."); + for (const String &E : dependencies) { + Vector<String> dependency_tags = E.split("."); bool all_tags_met = true; - for (int i = 0; i < tags.size(); i++) { - String tag = tags[i].strip_edges(); + for (int i = 0; i < dependency_tags.size(); i++) { + String tag = dependency_tags[i].strip_edges(); if (!p_features.has(tag)) { all_tags_met = false; break; @@ -129,13 +107,12 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p if (all_tags_met) { Dictionary dependency = config->get_value("dependencies", E); for (const Variant *key = dependency.next(nullptr); key; key = dependency.next(key)) { - String library_path = *key; + String dependency_path = *key; String target_path = dependency[*key]; - if (!library_path.begins_with("res://")) { - print_line("Skipping export of out-of-project library " + library_path); - continue; + if (dependency_path.is_relative_path()) { + dependency_path = p_path.get_base_dir().path_join(dependency_path); } - add_shared_object(library_path, tags, target_path); + add_shared_object(dependency_path, dependency_tags, target_path); } break; } diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 0af2a13df2..c8b80db334 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -30,6 +30,7 @@ #include "node_3d_editor_gizmos.h" +#include "core/config/project_settings.h" #include "core/math/convex_hull.h" #include "core/math/geometry_2d.h" #include "core/math/geometry_3d.h" @@ -1732,6 +1733,24 @@ Camera3DGizmoPlugin::Camera3DGizmoPlugin() { create_handle_material("handles"); } +Size2i Camera3DGizmoPlugin::_get_viewport_size(Camera3D *p_camera) { + Viewport *viewport = p_camera->get_viewport(); + + Window *window = Object::cast_to<Window>(viewport); + if (window) { + return window->get_size(); + } + + SubViewport *sub_viewport = Object::cast_to<SubViewport>(viewport); + ERR_FAIL_NULL_V(sub_viewport, Size2i()); + + if (sub_viewport == EditorNode::get_singleton()->get_scene_root()) { + return Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); + } + + return sub_viewport->get_size(); +} + bool Camera3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { return Object::cast_to<Camera3D>(p_spatial) != nullptr; } @@ -1830,6 +1849,10 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Ref<Material> material = get_material("camera_material", p_gizmo); + const Size2i viewport_size = _get_viewport_size(camera); + const real_t viewport_aspect = viewport_size.x > 0 && viewport_size.y > 0 ? viewport_size.aspect() : 1.0; + const Size2 size_factor = viewport_aspect > 1.0 ? Size2(1.0, 1.0 / viewport_aspect) : Size2(viewport_aspect, 1.0); + #define ADD_TRIANGLE(m_a, m_b, m_c) \ { \ lines.push_back(m_a); \ @@ -1857,10 +1880,11 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { // The real FOV is halved for accurate representation float fov = camera->get_fov() / 2.0; - Vector3 side = Vector3(Math::sin(Math::deg_to_rad(fov)), 0, -Math::cos(Math::deg_to_rad(fov))); - Vector3 nside = side; - nside.x = -nside.x; - Vector3 up = Vector3(0, side.x, 0); + const float hsize = Math::sin(Math::deg_to_rad(fov)); + const float depth = -Math::cos(Math::deg_to_rad(fov)); + Vector3 side = Vector3(hsize * size_factor.x, 0, depth); + Vector3 nside = Vector3(-side.x, side.y, side.z); + Vector3 up = Vector3(0, hsize * size_factor.y, 0); ADD_TRIANGLE(Vector3(), side + up, side - up); ADD_TRIANGLE(Vector3(), nside + up, nside - up); @@ -1868,18 +1892,18 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { ADD_TRIANGLE(Vector3(), side - up, nside - up); handles.push_back(side); - side.x *= 0.25; - nside.x *= 0.25; - Vector3 tup(0, up.y * 3 / 2, side.z); + side.x = MIN(side.x, hsize * 0.25); + nside.x = -side.x; + Vector3 tup(0, up.y + hsize / 2, side.z); ADD_TRIANGLE(tup, side + up, nside + up); - } break; + case Camera3D::PROJECTION_ORTHOGONAL: { float size = camera->get_size(); float hsize = size * 0.5; - Vector3 right(hsize, 0, 0); - Vector3 up(0, hsize, 0); + Vector3 right(hsize * size_factor.x, 0, 0); + Vector3 up(0, hsize * size_factor.y, 0); Vector3 back(0, 0, -1.0); Vector3 front(0, 0, 0); @@ -1890,18 +1914,19 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { handles.push_back(right + back); - right.x *= 0.25; - Vector3 tup(0, up.y * 3 / 2, back.z); + right.x = MIN(right.x, hsize * 0.25); + Vector3 tup(0, up.y + hsize / 2, back.z); ADD_TRIANGLE(tup, right + up + back, -right + up + back); } break; + case Camera3D::PROJECTION_FRUSTUM: { float hsize = camera->get_size() / 2.0; Vector3 side = Vector3(hsize, 0, -camera->get_near()).normalized(); - Vector3 nside = side; - nside.x = -nside.x; - Vector3 up = Vector3(0, side.x, 0); + side.x *= size_factor.x; + Vector3 nside = Vector3(-side.x, side.y, side.z); + Vector3 up = Vector3(0, hsize * size_factor.y, 0); Vector3 offset = Vector3(camera->get_frustum_offset().x, camera->get_frustum_offset().y, 0.0); ADD_TRIANGLE(Vector3(), side + up + offset, side - up + offset); @@ -1909,11 +1934,11 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { ADD_TRIANGLE(Vector3(), side + up + offset, nside + up + offset); ADD_TRIANGLE(Vector3(), side - up + offset, nside - up + offset); - side.x *= 0.25; - nside.x *= 0.25; - Vector3 tup(0, up.y * 3 / 2, side.z); + side.x = MIN(side.x, hsize * 0.25); + nside.x = -side.x; + Vector3 tup(0, up.y + hsize / 2, side.z); ADD_TRIANGLE(tup + offset, side + up + offset, nside + up + offset); - } + } break; } #undef ADD_TRIANGLE @@ -1921,7 +1946,10 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->add_lines(lines, material); p_gizmo->add_collision_segments(lines); - p_gizmo->add_handles(handles, get_material("handles")); + + if (!handles.is_empty()) { + p_gizmo->add_handles(handles, get_material("handles")); + } } ////// diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h index d7e3e03f61..60d44ad787 100644 --- a/editor/plugins/node_3d_editor_gizmos.h +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -264,6 +264,9 @@ public: class Camera3DGizmoPlugin : public EditorNode3DGizmoPlugin { GDCLASS(Camera3DGizmoPlugin, EditorNode3DGizmoPlugin); +private: + static Size2i _get_viewport_size(Camera3D *p_camera); + public: bool has_gizmo(Node3D *p_spatial) override; String get_gizmo_name() const override; diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index df1fd52b69..112a3fa51b 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -86,6 +86,177 @@ constexpr real_t MAX_Z = 1000000.0; constexpr real_t MIN_FOV = 0.01; constexpr real_t MAX_FOV = 179; +void ViewportNavigationControl::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (!is_connected("mouse_exited", callable_mp(this, &ViewportNavigationControl::_on_mouse_exited))) { + connect("mouse_exited", callable_mp(this, &ViewportNavigationControl::_on_mouse_exited)); + } + if (!is_connected("mouse_entered", callable_mp(this, &ViewportNavigationControl::_on_mouse_entered))) { + connect("mouse_entered", callable_mp(this, &ViewportNavigationControl::_on_mouse_entered)); + } + } break; + + case NOTIFICATION_DRAW: { + if (viewport != nullptr) { + _draw(); + _update_navigation(); + } + } break; + } +} + +void ViewportNavigationControl::_draw() { + if (nav_mode == Node3DEditorViewport::NAVIGATION_NONE) { + return; + } + + Vector2 center = get_size() / 2.0; + float radius = get_size().x / 2.0; + + const bool focused = focused_index != -1; + draw_circle(center, radius, Color(0.5, 0.5, 0.5, focused || hovered ? 0.35 : 0.15)); + + const Color c = focused ? Color(0.9, 0.9, 0.9, 0.9) : Color(0.5, 0.5, 0.5, 0.25); + + Vector2 circle_pos = focused ? center.move_toward(focused_pos, radius) : center; + + draw_circle(circle_pos, AXIS_CIRCLE_RADIUS, c); + draw_circle(circle_pos, AXIS_CIRCLE_RADIUS * 0.8, c.darkened(0.4)); +} + +void ViewportNavigationControl::_process_click(int p_index, Vector2 p_position, bool p_pressed) { + hovered = false; + queue_redraw(); + + if (focused_index != -1 && focused_index != p_index) { + return; + } + if (p_pressed) { + if (p_position.distance_to(get_size() / 2.0) < get_size().x / 2.0) { + focused_pos = p_position; + focused_index = p_index; + queue_redraw(); + } + } else { + focused_index = -1; + if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_CAPTURED) { + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + Input::get_singleton()->warp_mouse(focused_mouse_start); + } + } +} + +void ViewportNavigationControl::_process_drag(int p_index, Vector2 p_position, Vector2 p_relative_position) { + if (focused_index == p_index) { + if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_VISIBLE) { + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED); + focused_mouse_start = p_position; + } + focused_pos += p_relative_position; + queue_redraw(); + } +} + +void ViewportNavigationControl::gui_input(const Ref<InputEvent> &p_event) { + // Mouse events + const Ref<InputEventMouseButton> mouse_button = p_event; + if (mouse_button.is_valid() && mouse_button->get_button_index() == MouseButton::LEFT) { + _process_click(100, mouse_button->get_position(), mouse_button->is_pressed()); + } + + const Ref<InputEventMouseMotion> mouse_motion = p_event; + if (mouse_motion.is_valid()) { + _process_drag(100, mouse_motion->get_global_position(), viewport->_get_warped_mouse_motion(mouse_motion)); + } + + // Touch events + const Ref<InputEventScreenTouch> screen_touch = p_event; + if (screen_touch.is_valid()) { + _process_click(screen_touch->get_index(), screen_touch->get_position(), screen_touch->is_pressed()); + } + + const Ref<InputEventScreenDrag> screen_drag = p_event; + if (screen_drag.is_valid()) { + _process_drag(screen_drag->get_index(), screen_drag->get_position(), screen_drag->get_relative()); + } +} + +void ViewportNavigationControl::_update_navigation() { + if (focused_index == -1) { + return; + } + + Vector2 delta = focused_pos - (get_size() / 2.0); + Vector2 delta_normalized = delta.normalized(); + switch (nav_mode) { + case Node3DEditorViewport::NavigationMode::NAVIGATION_MOVE: { + real_t speed_multiplier = MIN(delta.length() / (get_size().x * 100.0), 3.0); + real_t speed = viewport->freelook_speed * speed_multiplier; + + const Node3DEditorViewport::FreelookNavigationScheme navigation_scheme = (Node3DEditorViewport::FreelookNavigationScheme)EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_navigation_scheme").operator int(); + + Vector3 forward; + if (navigation_scheme == Node3DEditorViewport::FreelookNavigationScheme::FREELOOK_FULLY_AXIS_LOCKED) { + // Forward/backward keys will always go straight forward/backward, never moving on the Y axis. + forward = Vector3(0, 0, delta_normalized.y).rotated(Vector3(0, 1, 0), viewport->camera->get_rotation().y); + } else { + // Forward/backward keys will be relative to the camera pitch. + forward = viewport->camera->get_transform().basis.xform(Vector3(0, 0, delta_normalized.y)); + } + + const Vector3 right = viewport->camera->get_transform().basis.xform(Vector3(delta_normalized.x, 0, 0)); + + const Vector3 direction = forward + right; + const Vector3 motion = direction * speed; + viewport->cursor.pos += motion; + viewport->cursor.eye_pos += motion; + } break; + + case Node3DEditorViewport::NavigationMode::NAVIGATION_LOOK: { + real_t speed_multiplier = MIN(delta.length() / (get_size().x * 2.5), 3.0); + real_t speed = viewport->freelook_speed * speed_multiplier; + viewport->_nav_look(nullptr, delta_normalized * speed); + } break; + + case Node3DEditorViewport::NAVIGATION_PAN: { + real_t speed_multiplier = MIN(delta.length() / (get_size().x), 3.0); + real_t speed = viewport->freelook_speed * speed_multiplier; + viewport->_nav_pan(nullptr, -delta_normalized * speed); + } break; + case Node3DEditorViewport::NAVIGATION_ZOOM: { + real_t speed_multiplier = MIN(delta.length() / (get_size().x), 3.0); + real_t speed = viewport->freelook_speed * speed_multiplier; + viewport->_nav_zoom(nullptr, delta_normalized * speed); + } break; + case Node3DEditorViewport::NAVIGATION_ORBIT: { + real_t speed_multiplier = MIN(delta.length() / (get_size().x), 3.0); + real_t speed = viewport->freelook_speed * speed_multiplier; + viewport->_nav_orbit(nullptr, delta_normalized * speed); + } break; + case Node3DEditorViewport::NAVIGATION_NONE: { + } break; + } +} + +void ViewportNavigationControl::_on_mouse_entered() { + hovered = true; + queue_redraw(); +} + +void ViewportNavigationControl::_on_mouse_exited() { + hovered = false; + queue_redraw(); +} + +void ViewportNavigationControl::set_navigation_mode(Node3DEditorViewport::NavigationMode p_nav_mode) { + nav_mode = p_nav_mode; +} + +void ViewportNavigationControl::set_viewport(Node3DEditorViewport *p_viewport) { + viewport = p_viewport; +} + void ViewportRotationControl::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -120,7 +291,7 @@ void ViewportRotationControl::_draw() { const Vector2i center = get_size() / 2.0; const real_t radius = get_size().x / 2.0; - if (focused_axis > -2 || orbiting) { + if (focused_axis > -2 || orbiting_index != -1) { draw_circle(center, radius, Color(0.5, 0.5, 0.5, 0.25)); } @@ -191,41 +362,63 @@ void ViewportRotationControl::_get_sorted_axis(Vector<Axis2D> &r_axis) { r_axis.sort_custom<Axis2DCompare>(); } +void ViewportRotationControl::_process_click(int p_index, Vector2 p_position, bool p_pressed) { + if (orbiting_index != -1 && orbiting_index != p_index) { + return; + } + if (p_pressed) { + if (p_position.distance_to(get_size() / 2.0) < get_size().x / 2.0) { + orbiting_index = p_index; + } + } else { + if (focused_axis > -1) { + viewport->_menu_option(axis_menu_options[focused_axis]); + _update_focus(); + } + orbiting_index = -1; + if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_CAPTURED) { + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + Input::get_singleton()->warp_mouse(orbiting_mouse_start); + } + } +} + +void ViewportRotationControl::_process_drag(Ref<InputEventWithModifiers> p_event, int p_index, Vector2 p_position, Vector2 p_relative_position) { + if (orbiting_index == p_index) { + if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_VISIBLE) { + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED); + orbiting_mouse_start = p_position; + } + viewport->_nav_orbit(p_event, p_relative_position); + focused_axis = -1; + } else { + _update_focus(); + } +} + void ViewportRotationControl::gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); + // Mouse events const Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) { - Vector2 pos = mb->get_position(); - if (mb->is_pressed()) { - if (pos.distance_to(get_size() / 2.0) < get_size().x / 2.0) { - orbiting = true; - } - } else { - if (focused_axis > -1) { - viewport->_menu_option(axis_menu_options[focused_axis]); - _update_focus(); - } - orbiting = false; - if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_CAPTURED) { - Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); - Input::get_singleton()->warp_mouse(orbiting_mouse_start); - } - } + _process_click(100, mb->get_position(), mb->is_pressed()); } const Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - if (orbiting) { - if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_VISIBLE) { - Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED); - orbiting_mouse_start = mm->get_global_position(); - } - viewport->_nav_orbit(mm, viewport->_get_warped_mouse_motion(mm)); - focused_axis = -1; - } else { - _update_focus(); - } + _process_drag(mm, 100, mm->get_global_position(), viewport->_get_warped_mouse_motion(mm)); + } + + // Touch events + const Ref<InputEventScreenTouch> screen_touch = p_event; + if (screen_touch.is_valid()) { + _process_click(screen_touch->get_index(), screen_touch->get_position(), screen_touch->is_pressed()); + } + + const Ref<InputEventScreenDrag> screen_drag = p_event; + if (screen_drag.is_valid()) { + _process_drag(screen_drag, screen_drag->get_index(), screen_drag->get_position(), screen_drag->get_relative()); } } @@ -353,6 +546,8 @@ void Node3DEditorViewport::_update_camera(real_t p_interp_delta) { update_transform_gizmo_view(); rotation_control->queue_redraw(); + position_control->queue_redraw(); + look_control->queue_redraw(); spatial_editor->update_grid(); } } @@ -2091,7 +2286,7 @@ void Node3DEditorViewport::_nav_pan(Ref<InputEventWithModifiers> p_event, const const NavigationScheme nav_scheme = (NavigationScheme)EDITOR_GET("editors/3d/navigation/navigation_scheme").operator int(); real_t pan_speed = 1 / 150.0; - if (nav_scheme == NAVIGATION_MAYA && p_event->is_shift_pressed()) { + if (p_event.is_valid() && nav_scheme == NAVIGATION_MAYA && p_event->is_shift_pressed()) { pan_speed *= 10; } @@ -2115,7 +2310,7 @@ void Node3DEditorViewport::_nav_zoom(Ref<InputEventWithModifiers> p_event, const const NavigationScheme nav_scheme = (NavigationScheme)EDITOR_GET("editors/3d/navigation/navigation_scheme").operator int(); real_t zoom_speed = 1 / 80.0; - if (nav_scheme == NAVIGATION_MAYA && p_event->is_shift_pressed()) { + if (p_event.is_valid() && nav_scheme == NAVIGATION_MAYA && p_event->is_shift_pressed()) { zoom_speed *= 10; } @@ -2448,6 +2643,8 @@ void Node3DEditorViewport::_notification(int p_what) { } call_deferred(SNAME("update_transform_gizmo_view")); rotation_control->set_visible(EDITOR_GET("editors/3d/navigation/show_viewport_rotation_gizmo")); + position_control->set_visible(EDITOR_GET("editors/3d/navigation/show_viewport_navigation_gizmo")); + look_control->set_visible(EDITOR_GET("editors/3d/navigation/show_viewport_navigation_gizmo")); } break; case NOTIFICATION_RESIZED: { @@ -3370,6 +3567,8 @@ void Node3DEditorViewport::_toggle_camera_preview(bool p_activate) { ERR_FAIL_COND(!p_activate && !previewing); rotation_control->set_visible(!p_activate); + position_control->set_visible(!p_activate); + look_control->set_visible(!p_activate); if (!p_activate) { previewing->disconnect("tree_exiting", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); @@ -3391,6 +3590,8 @@ void Node3DEditorViewport::_toggle_camera_preview(bool p_activate) { void Node3DEditorViewport::_toggle_cinema_preview(bool p_activate) { previewing_cinema = p_activate; rotation_control->set_visible(!p_activate); + position_control->set_visible(!p_activate); + look_control->set_visible(!p_activate); if (!previewing_cinema) { if (previewing != nullptr) { @@ -4874,6 +5075,14 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p preview_node = nullptr; + bottom_center_vbox = memnew(VBoxContainer); + bottom_center_vbox->set_anchors_preset(LayoutPreset::PRESET_CENTER); + bottom_center_vbox->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -20 * EDSCALE); + bottom_center_vbox->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -10 * EDSCALE); + bottom_center_vbox->set_h_grow_direction(GROW_DIRECTION_BOTH); + bottom_center_vbox->set_v_grow_direction(GROW_DIRECTION_BEGIN); + surface->add_child(bottom_center_vbox); + info_label = memnew(Label); info_label->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -90 * EDSCALE); info_label->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -90 * EDSCALE); @@ -4894,23 +5103,18 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p previewing_cinema = false; locked_label = memnew(Label); - locked_label->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -20 * EDSCALE); - locked_label->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -10 * EDSCALE); - locked_label->set_h_grow_direction(GROW_DIRECTION_END); - locked_label->set_v_grow_direction(GROW_DIRECTION_BEGIN); locked_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); - surface->add_child(locked_label); + locked_label->set_h_size_flags(SIZE_SHRINK_CENTER); + bottom_center_vbox->add_child(locked_label); locked_label->set_text(TTR("View Rotation Locked")); locked_label->hide(); zoom_limit_label = memnew(Label); - zoom_limit_label->set_anchors_and_offsets_preset(LayoutPreset::PRESET_BOTTOM_LEFT); - zoom_limit_label->set_offset(Side::SIDE_TOP, -28 * EDSCALE); zoom_limit_label->set_text(TTR("To zoom further, change the camera's clipping planes (View -> Settings...)")); zoom_limit_label->set_name("ZoomLimitMessageLabel"); zoom_limit_label->add_theme_color_override("font_color", Color(1, 1, 1, 1)); zoom_limit_label->hide(); - surface->add_child(zoom_limit_label); + bottom_center_vbox->add_child(zoom_limit_label); preview_material_label = memnew(Label); preview_material_label->set_anchors_and_offsets_preset(LayoutPreset::PRESET_BOTTOM_LEFT); @@ -4941,6 +5145,30 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p // Prevent visible spacing between frame time labels. top_right_vbox->add_theme_constant_override("separation", 0); + const int navigation_control_size = 150; + + position_control = memnew(ViewportNavigationControl); + position_control->set_navigation_mode(Node3DEditorViewport::NAVIGATION_MOVE); + position_control->set_custom_minimum_size(Size2(navigation_control_size, navigation_control_size) * EDSCALE); + position_control->set_h_size_flags(SIZE_SHRINK_END); + position_control->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0 * EDSCALE); + position_control->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -navigation_control_size * EDSCALE); + position_control->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_BEGIN, navigation_control_size * EDSCALE); + position_control->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0 * EDSCALE); + position_control->set_viewport(this); + surface->add_child(position_control); + + look_control = memnew(ViewportNavigationControl); + look_control->set_navigation_mode(Node3DEditorViewport::NAVIGATION_LOOK); + look_control->set_custom_minimum_size(Size2(navigation_control_size, navigation_control_size) * EDSCALE); + look_control->set_h_size_flags(SIZE_SHRINK_END); + look_control->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -navigation_control_size * EDSCALE); + look_control->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -navigation_control_size * EDSCALE); + look_control->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0 * EDSCALE); + look_control->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0 * EDSCALE); + look_control->set_viewport(this); + surface->add_child(look_control); + rotation_control = memnew(ViewportRotationControl); rotation_control->set_custom_minimum_size(Size2(80, 80) * EDSCALE); rotation_control->set_h_size_flags(SIZE_SHRINK_END); @@ -7249,6 +7477,8 @@ void Node3DEditor::_notification(int p_what) { sun_state->set_custom_minimum_size(sun_vb->get_combined_minimum_size()); environ_state->set_custom_minimum_size(environ_vb->get_combined_minimum_size()); + + EditorNode::get_singleton()->connect("project_settings_changed", callable_mp(this, &Node3DEditor::update_all_gizmos).bind(Variant())); } break; case NOTIFICATION_ENTER_TREE: { @@ -8191,7 +8421,8 @@ Node3DEditor::Node3DEditor() { EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d/manipulator_gizmo_size", PROPERTY_HINT_RANGE, "16,160,1")); EDITOR_DEF("editors/3d/manipulator_gizmo_opacity", 0.9); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::FLOAT, "editors/3d/manipulator_gizmo_opacity", PROPERTY_HINT_RANGE, "0,1,0.01")); - EDITOR_DEF("editors/3d/navigation/show_viewport_rotation_gizmo", true); + EDITOR_DEF_RST("editors/3d/navigation/show_viewport_rotation_gizmo", true); + EDITOR_DEF_RST("editors/3d/navigation/show_viewport_navigation_gizmo", DisplayServer::get_singleton()->is_touchscreen_available()); current_hover_gizmo_handle = -1; current_hover_gizmo_handle_secondary = false; diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index b7ac718182..ed555d86c3 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -57,6 +57,7 @@ class SubViewport; class SubViewportContainer; class VSplitContainer; class WorldEnvironment; +class ViewportNavigationControl; class ViewportRotationControl : public Control { GDCLASS(ViewportRotationControl, Control); @@ -77,7 +78,7 @@ class ViewportRotationControl : public Control { Vector<Color> axis_colors; Vector<int> axis_menu_options; Vector2i orbiting_mouse_start; - bool orbiting = false; + int orbiting_index = -1; int focused_axis = -2; const float AXIS_CIRCLE_RADIUS = 8.0f * EDSCALE; @@ -90,6 +91,8 @@ protected: void _get_sorted_axis(Vector<Axis2D> &r_axis); void _update_focus(); void _on_mouse_exited(); + void _process_click(int p_index, Vector2 p_position, bool p_pressed); + void _process_drag(Ref<InputEventWithModifiers> p_event, int p_index, Vector2 p_position, Vector2 p_relative_position); public: void set_viewport(Node3DEditorViewport *p_viewport); @@ -98,6 +101,7 @@ public: class Node3DEditorViewport : public Control { GDCLASS(Node3DEditorViewport, Control); friend class Node3DEditor; + friend class ViewportNavigationControl; friend class ViewportRotationControl; enum { VIEW_TOP, @@ -236,6 +240,9 @@ private: Label *preview_material_label_desc = nullptr; VBoxContainer *top_right_vbox = nullptr; + VBoxContainer *bottom_center_vbox = nullptr; + ViewportNavigationControl *position_control = nullptr; + ViewportNavigationControl *look_control = nullptr; ViewportRotationControl *rotation_control = nullptr; Gradient *frame_time_gradient = nullptr; Label *cpu_time_label = nullptr; @@ -297,7 +304,8 @@ private: NAVIGATION_PAN, NAVIGATION_ZOOM, NAVIGATION_ORBIT, - NAVIGATION_LOOK + NAVIGATION_LOOK, + NAVIGATION_MOVE }; enum TransformMode { TRANSFORM_NONE, @@ -916,4 +924,31 @@ public: ~Node3DEditorPlugin(); }; +class ViewportNavigationControl : public Control { + GDCLASS(ViewportNavigationControl, Control); + + Node3DEditorViewport *viewport = nullptr; + Vector2i focused_mouse_start; + Vector2 focused_pos; + bool hovered = false; + int focused_index = -1; + Node3DEditorViewport::NavigationMode nav_mode = Node3DEditorViewport::NavigationMode::NAVIGATION_NONE; + + const float AXIS_CIRCLE_RADIUS = 30.0f * EDSCALE; + +protected: + void _notification(int p_what); + virtual void gui_input(const Ref<InputEvent> &p_event) override; + void _draw(); + void _on_mouse_entered(); + void _on_mouse_exited(); + void _process_click(int p_index, Vector2 p_position, bool p_pressed); + void _process_drag(int p_index, Vector2 p_position, Vector2 p_relative_position); + void _update_navigation(); + +public: + void set_navigation_mode(Node3DEditorViewport::NavigationMode p_nav_mode); + void set_viewport(Node3DEditorViewport *p_viewport); +}; + #endif // NODE_3D_EDITOR_PLUGIN_H diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 5a7b0321b7..63ca78d6c0 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -240,38 +240,63 @@ void Path3DGizmo::redraw() { return; } - Vector<Vector3> v3a = c->tessellate(); - //Vector<Vector3> v3a=c->get_baked_points(); + real_t interval = 0.1; + const real_t length = c->get_baked_length(); - int v3s = v3a.size(); - if (v3s == 0) { - return; - } - Vector<Vector3> v3p; - const Vector3 *r = v3a.ptr(); - - // BUG: the following won't work when v3s, avoid drawing as a temporary workaround. - for (int i = 0; i < v3s - 1; i++) { - v3p.push_back(r[i]); - v3p.push_back(r[i + 1]); - //v3p.push_back(r[i]); - //v3p.push_back(r[i]+Vector3(0,0.2,0)); - } + // 1. Draw curve and bones. + if (length > CMP_EPSILON) { + const int sample_count = int(length / interval) + 2; + interval = length / (sample_count - 1); // Recalculate real interval length. + + Vector<Transform3D> frames; + frames.resize(sample_count); + + { + Transform3D *w = frames.ptrw(); + + for (int i = 0; i < sample_count; i++) { + w[i] = c->sample_baked_with_rotation(i * interval, true, true); + } + } + + const Transform3D *r = frames.ptr(); + Vector<Vector3> v3p; + for (int i = 0; i < sample_count - 1; i++) { + const Vector3 p1 = r[i].origin; + const Vector3 p2 = r[i + 1].origin; + const Vector3 side = r[i].basis.get_column(0); + const Vector3 up = r[i].basis.get_column(1); + const Vector3 forward = r[i].basis.get_column(2); + + // Curve segment. + v3p.push_back(p1); + v3p.push_back(p2); + + // Fish Bone. + v3p.push_back(p1); + v3p.push_back(p1 + (side - forward) * 0.06); + + v3p.push_back(p1); + v3p.push_back(p1 + (-side - forward) * 0.06); + + v3p.push_back(p1); + v3p.push_back(p1 + up * 0.03); + } - if (v3p.size() > 1) { add_lines(v3p, path_material); add_collision_segments(v3p); } + // 2. Draw handles. if (Path3DEditorPlugin::singleton->get_edited_path() == path) { - v3p.clear(); + Vector<Vector3> v3p; Vector<Vector3> handle_points; Vector<Vector3> sec_handle_points; for (int i = 0; i < c->get_point_count(); i++) { Vector3 p = c->get_point_position(i); handle_points.push_back(p); - // push Out points first so they get selected if the In and Out points are on top of each other. + // Push out points first so they get selected if the In and Out points are on top of each other. if (i < c->get_point_count() - 1) { v3p.push_back(p); v3p.push_back(p + c->get_point_out(i)); diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp index e266d26b73..f892f3637d 100644 --- a/editor/plugins/tiles/atlas_merging_dialog.cpp +++ b/editor/plugins/tiles/atlas_merging_dialog.cpp @@ -236,7 +236,7 @@ void AtlasMergingDialog::update_tile_set(Ref<TileSet> p_tile_set) { if (atlas_source.is_valid()) { Ref<Texture2D> texture = atlas_source->get_texture(); if (texture.is_valid()) { - String item_text = vformat("%s (id:%d)", texture->get_path().get_file(), source_id); + String item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id); atlas_merging_atlases_list->add_item(item_text, texture); atlas_merging_atlases_list->set_item_metadata(-1, source_id); } diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index e622a0817a..29578aa560 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -156,7 +156,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() { // Common to all type of sources. if (!source->get_name().is_empty()) { - item_text = vformat(TTR("%s (id:%d)"), source->get_name(), source_id); + item_text = vformat(TTR("%s (ID: %d)"), source->get_name(), source_id); } // Atlas source. @@ -165,7 +165,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() { texture = atlas_source->get_texture(); if (item_text.is_empty()) { if (texture.is_valid()) { - item_text = vformat("%s (ID: %d)", texture->get_path().get_file(), source_id); + item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id); } else { item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id); } @@ -1164,7 +1164,7 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vecto } // Get surrounding tiles (handles different tile shapes). - TypedArray<Vector2i> around = tile_map->get_surrounding_tiles(coords); + TypedArray<Vector2i> around = tile_map->get_surrounding_cells(coords); for (int i = 0; i < around.size(); i++) { to_check.push_back(around[i]); } @@ -2547,7 +2547,7 @@ RBSet<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i output.insert(coords); // Get surrounding tiles (handles different tile shapes). - TypedArray<Vector2i> around = tile_map->get_surrounding_tiles(coords); + TypedArray<Vector2i> around = tile_map->get_surrounding_cells(coords); for (int i = 0; i < around.size(); i++) { to_check.push_back(around[i]); } diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp index 7058b28e68..b31fb1aa58 100644 --- a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp +++ b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp @@ -36,7 +36,7 @@ #include "editor/editor_undo_redo_manager.h" #include "scene/gui/separator.h" -void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, Object *p_item_list, MouseButton p_mouse_button_index) { +void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index, Object *p_item_list) { if (p_mouse_button_index != MouseButton::RIGHT) { return; } @@ -77,7 +77,7 @@ void TileProxiesManagerDialog::_delete_selected_bindings() { Vector<int> alternative_level_selected = alternative_level_list->get_selected_items(); for (int i = 0; i < alternative_level_selected.size(); i++) { Array key = alternative_level_list->get_item_metadata(alternative_level_selected[i]); - Array val = tile_set->get_coords_level_tile_proxy(key[0], key[1]); + Array val = tile_set->get_alternative_level_tile_proxy(key[0], key[1], key[2]); undo_redo->add_do_method(*tile_set, "remove_alternative_level_tile_proxy", key[0], key[1], key[2]); undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", key[0], key[1], key[2], val[0], val[1], val[2]); } diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.h b/editor/plugins/tiles/tile_proxies_manager_dialog.h index e2363eb809..09c8068336 100644 --- a/editor/plugins/tiles/tile_proxies_manager_dialog.h +++ b/editor/plugins/tiles/tile_proxies_manager_dialog.h @@ -61,7 +61,7 @@ private: EditorPropertyInteger *alternative_to_property_editor = nullptr; PopupMenu *popup_menu = nullptr; - void _right_clicked(int p_item, Vector2 p_local_mouse_pos, Object *p_item_list, MouseButton p_mouse_button_index); + void _right_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index, Object *p_item_list); void _menu_id_pressed(int p_id); void _delete_selected_bindings(); void _update_lists(); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index ae7570e161..7ed84423bc 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -2345,6 +2345,15 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { tile_set_changed_needs_update = false; } } break; + + case NOTIFICATION_EXIT_TREE: { + for (KeyValue<String, TileDataEditor *> &E : tile_data_editors) { + Control *toolbar = E.value->get_toolbar(); + if (toolbar->get_parent() == tool_settings_tile_data_toolbar_container) { + tool_settings_tile_data_toolbar_container->remove_child(toolbar); + } + } + } break; } } diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index b24c5059b0..dbecf52398 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -151,7 +151,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) { // Common to all type of sources. if (!source->get_name().is_empty()) { - item_text = vformat(TTR("%s (id:%d)"), source->get_name(), source_id); + item_text = vformat(TTR("%s (ID: %d)"), source->get_name(), source_id); } // Atlas source. @@ -160,7 +160,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) { texture = atlas_source->get_texture(); if (item_text.is_empty()) { if (texture.is_valid()) { - item_text = vformat("%s (ID: %d)", texture->get_path().get_file(), source_id); + item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id); } else { item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id); } diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index 86aa897c78..369a59c443 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -988,7 +988,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { metadata_dialog->set_title(TTR("Create Version Control Metadata")); metadata_dialog->set_min_size(Size2(200, 40)); metadata_dialog->get_ok_button()->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_create_vcs_metadata_files)); - version_control_actions->add_child(metadata_dialog); + add_child(metadata_dialog); VBoxContainer *metadata_vb = memnew(VBoxContainer); metadata_dialog->add_child(metadata_vb); @@ -1017,7 +1017,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { set_up_dialog->set_min_size(Size2(600, 100)); set_up_dialog->add_cancel_button("Cancel"); set_up_dialog->set_hide_on_ok(true); - version_control_actions->add_child(set_up_dialog); + add_child(set_up_dialog); Button *set_up_apply_button = set_up_dialog->get_ok_button(); set_up_apply_button->set_text(TTR("Apply")); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index f32e0bdfa2..9990d5c06f 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -5209,6 +5209,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("NodePositionWorld", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "node_position_world", "NODE_POSITION_WORLD"), { "node_position_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("CameraPositionWorld", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "camera_position_world", "CAMERA_POSITION_WORLD"), { "camera_position_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("CameraDirectionWorld", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "camera_direction_world", "CAMERA_DIRECTION_WORLD"), { "camera_direction_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("CameraVisibleLayers", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "camera_visible_layers", "CAMERA_VISIBLE_LAYERS"), { "camera_visible_layers" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("NodePositionView", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "node_position_view", "NODE_POSITION_VIEW"), { "node_position_view" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Binormal", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal", "BINORMAL"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); @@ -5228,6 +5229,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("NodePositionWorld", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "node_position_world", "NODE_POSITION_WORLD"), { "node_position_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("CameraPositionWorld", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "camera_position_world", "CAMERA_POSITION_WORLD"), { "camera_position_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("CameraDirectionWorld", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "camera_direction_world", "CAMERA_DIRECTION_WORLD"), { "camera_direction_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("CameraVisibleLayers", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "camera_visible_layers", "CAMERA_VISIBLE_LAYERS"), { "camera_visible_layers" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("NodePositionView", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "node_position_view", "NODE_POSITION_VIEW"), { "node_position_view" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Albedo", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "albedo", "ALBEDO"), { "albedo" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 28111bed58..1e917e6b3d 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -274,10 +274,12 @@ void ProjectSettingsEditor::_add_feature_overrides() { presets.insert("s3tc"); presets.insert("etc"); presets.insert("etc2"); + presets.insert("editor"); + presets.insert("template_debug"); + presets.insert("template_release"); presets.insert("debug"); presets.insert("release"); - presets.insert("editor"); - presets.insert("standalone"); + presets.insert("template"); presets.insert("32"); presets.insert("64"); presets.insert("movie"); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 0ebd8e610a..96688a3614 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -2453,9 +2453,12 @@ void SceneTreeDock::_new_scene_from(String p_file) { Node *copy = base->duplicate_from_editor(duplimap); if (copy) { + // Handle Unique Nodes. for (int i = 0; i < copy->get_child_count(false); i++) { _set_node_owner_recursive(copy->get_child(i, false), copy); } + // Root node cannot ever be unique name in its own Scene! + copy->set_unique_name_in_owner(false); Ref<PackedScene> sdata = memnew(PackedScene); Error err = sdata->pack(copy); diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 4a3b0e979f..52f4ae553f 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -107,7 +107,15 @@ static Vector<String> _get_hierarchy(String p_class_name) { void ScriptCreateDialog::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + Ref<Texture2D> language_icon = get_theme_icon(ScriptServer::get_language(i)->get_type(), SNAME("EditorIcons")); + if (language_icon.is_valid()) { + language_menu->set_item_icon(i, language_icon); + } + } + String last_language = EditorSettings::get_singleton()->get_project_metadata("script_setup", "last_selected_language", ""); if (!last_language.is_empty()) { for (int i = 0; i < language_menu->get_item_count(); i++) { @@ -120,15 +128,9 @@ void ScriptCreateDialog::_notification(int p_what) { } else { language_menu->select(default_language); } - - [[fallthrough]]; - } - case NOTIFICATION_THEME_CHANGED: { - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - Ref<Texture2D> language_icon = get_theme_icon(ScriptServer::get_language(i)->get_type(), SNAME("EditorIcons")); - if (language_icon.is_valid()) { - language_menu->set_item_icon(i, language_icon); - } + if (EditorSettings::get_singleton()->has_meta("script_setup_use_script_templates")) { + is_using_templates = bool(EditorSettings::get_singleton()->get_meta("script_setup_use_script_templates")); + use_templates->set_pressed(is_using_templates); } path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); @@ -331,7 +333,12 @@ void ScriptCreateDialog::_template_changed(int p_template) { EditorSettings::get_singleton()->set_project_metadata("script_setup", "templates_dictionary", dic_templates_project); } else { // Save template info to editor dictionary (not a project template). - templates_dictionary[parent_name->get_text()] = sinfo.get_hash(); + Dictionary dic_templates; + if (EditorSettings::get_singleton()->has_meta("script_setup_templates_dictionary")) { + dic_templates = (Dictionary)EditorSettings::get_singleton()->get_meta("script_setup_templates_dictionary"); + } + dic_templates[parent_name->get_text()] = sinfo.get_hash(); + EditorSettings::get_singleton()->set_meta("script_setup_templates_dictionary", dic_templates); // Remove template from project dictionary as we last used an editor level template. Dictionary dic_templates_project = EditorSettings::get_singleton()->get_project_metadata("script_setup", "templates_dictionary", Dictionary()); if (dic_templates_project.has(parent_name->get_text())) { @@ -480,6 +487,7 @@ void ScriptCreateDialog::_built_in_pressed() { void ScriptCreateDialog::_use_template_pressed() { is_using_templates = use_templates->is_pressed(); + EditorSettings::get_singleton()->set_meta("script_setup_use_script_templates", is_using_templates); _update_dialog(); } @@ -597,6 +605,10 @@ void ScriptCreateDialog::_update_template_menu() { if (is_language_using_templates) { // Get the latest templates used for each type of node from project settings then global settings. Dictionary last_local_templates = EditorSettings::get_singleton()->get_project_metadata("script_setup", "templates_dictionary", Dictionary()); + Dictionary last_global_templates; + if (EditorSettings::get_singleton()->has_meta("script_setup_templates_dictionary")) { + last_global_templates = (Dictionary)EditorSettings::get_singleton()->get_meta("script_setup_templates_dictionary"); + } String inherits_base_type = parent_name->get_text(); // If it inherits from a script, get its parent class first. @@ -651,7 +663,7 @@ void ScriptCreateDialog::_update_template_menu() { // Check for last used template for this node in project settings then in global settings. if (last_local_templates.has(parent_name->get_text()) && t.get_hash() == String(last_local_templates[parent_name->get_text()])) { last_used_template = id; - } else if (last_used_template == -1 && templates_dictionary.has(parent_name->get_text()) && t.get_hash() == String(templates_dictionary[parent_name->get_text()])) { + } else if (last_used_template == -1 && last_global_templates.has(parent_name->get_text()) && t.get_hash() == String(last_global_templates[parent_name->get_text()])) { last_used_template = id; } t.id = id; diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h index fb1a49a1ca..25428508da 100644 --- a/editor/script_create_dialog.h +++ b/editor/script_create_dialog.h @@ -83,7 +83,6 @@ class ScriptCreateDialog : public ConfirmationDialog { int current_language; int default_language; bool re_check_path = false; - Dictionary templates_dictionary; Control *path_controls[2]; Control *name_controls[2]; diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp index 22a1d49422..b778262fed 100644 --- a/editor/shader_globals_editor.cpp +++ b/editor/shader_globals_editor.cpp @@ -69,28 +69,12 @@ static const char *global_var_type_names[RS::GLOBAL_VAR_TYPE_MAX] = { class ShaderGlobalsEditorInterface : public Object { GDCLASS(ShaderGlobalsEditorInterface, Object) - void _var_changed() { - emit_signal(SNAME("var_changed")); - } - -protected: - static void _bind_methods() { - ClassDB::bind_method("_var_changed", &ShaderGlobalsEditorInterface::_var_changed); - ADD_SIGNAL(MethodInfo("var_changed")); - } - - bool _set(const StringName &p_name, const Variant &p_value) { - Variant existing = RS::get_singleton()->global_shader_parameter_get(p_name); - - if (existing.get_type() == Variant::NIL) { - return false; - } - + void _set_var(const StringName &p_name, const Variant &p_value, const Variant &p_prev_value) { Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Set Shader Global Variable")); undo_redo->add_do_method(RS::get_singleton(), "global_shader_parameter_set", p_name, p_value); - undo_redo->add_undo_method(RS::get_singleton(), "global_shader_parameter_set", p_name, existing); + undo_redo->add_undo_method(RS::get_singleton(), "global_shader_parameter_set", p_name, p_prev_value); RS::GlobalShaderParameterType type = RS::get_singleton()->global_shader_parameter_get_type(p_name); Dictionary gv; gv["type"] = global_var_type_names[type]; @@ -111,8 +95,29 @@ protected: undo_redo->add_do_method(this, "_var_changed"); undo_redo->add_undo_method(this, "_var_changed"); block_update = true; - undo_redo->commit_action(false); + undo_redo->commit_action(); block_update = false; + } + + void _var_changed() { + emit_signal(SNAME("var_changed")); + } + +protected: + static void _bind_methods() { + ClassDB::bind_method("_set_var", &ShaderGlobalsEditorInterface::_set_var); + ClassDB::bind_method("_var_changed", &ShaderGlobalsEditorInterface::_var_changed); + ADD_SIGNAL(MethodInfo("var_changed")); + } + + bool _set(const StringName &p_name, const Variant &p_value) { + Variant existing = RS::get_singleton()->global_shader_parameter_get(p_name); + + if (existing.get_type() == Variant::NIL) { + return false; + } + + call_deferred("_set_var", p_name, p_value, existing); return true; } |