diff options
Diffstat (limited to 'editor')
-rw-r--r-- | editor/animation_track_editor.cpp | 75 | ||||
-rw-r--r-- | editor/animation_track_editor.h | 3 | ||||
-rw-r--r-- | editor/connections_dialog.cpp | 9 | ||||
-rw-r--r-- | editor/editor_file_system.cpp | 165 | ||||
-rw-r--r-- | editor/editor_file_system.h | 1 | ||||
-rw-r--r-- | editor/editor_inspector.cpp | 13 | ||||
-rw-r--r-- | editor/editor_inspector.h | 1 | ||||
-rw-r--r-- | editor/editor_node.cpp | 22 | ||||
-rw-r--r-- | editor/editor_node.h | 1 | ||||
-rw-r--r-- | editor/editor_properties_array_dict.cpp | 20 | ||||
-rw-r--r-- | editor/editor_properties_array_dict.h | 11 | ||||
-rw-r--r-- | editor/editor_resource_preview.cpp | 4 | ||||
-rw-r--r-- | editor/editor_resource_preview.h | 3 | ||||
-rw-r--r-- | editor/gui/editor_scene_tabs.cpp | 2 | ||||
-rw-r--r-- | editor/gui/scene_tree_editor.cpp | 24 | ||||
-rw-r--r-- | editor/gui/scene_tree_editor.h | 1 | ||||
-rw-r--r-- | editor/import_dock.cpp | 16 | ||||
-rw-r--r-- | editor/plugins/animation_player_editor_plugin.cpp | 14 | ||||
-rw-r--r-- | editor/plugins/skeleton_3d_editor_plugin.cpp | 4 | ||||
-rw-r--r-- | editor/project_settings_editor.cpp | 12 | ||||
-rw-r--r-- | editor/property_selector.cpp | 17 | ||||
-rw-r--r-- | editor/scene_tree_dock.cpp | 41 | ||||
-rw-r--r-- | editor/scene_tree_dock.h | 3 |
23 files changed, 292 insertions, 170 deletions
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 2d497a281f..58dd659e86 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -57,6 +57,9 @@ #include "scene/main/window.h" #include "servers/audio/audio_stream.h" +constexpr double FPS_DECIMAL = 1; +constexpr double SECOND_DECIMAL = 0.0001; + void AnimationTrackKeyEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj); ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationTrackKeyEdit::_key_ofs_changed); @@ -1322,7 +1325,7 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) { return; } - p_new_len = MAX(0.0001, p_new_len); + p_new_len = MAX(SECOND_DECIMAL, p_new_len); if (use_fps && animation->get_step() > 0) { p_new_len *= animation->get_step(); } @@ -1442,7 +1445,7 @@ void AnimationTimelineEdit::_notification(int p_what) { float l = animation->get_length(); if (l <= 0) { - l = 0.0001; // Avoid crashor. + l = SECOND_DECIMAL; // Avoid crashor. } Ref<Texture2D> hsize_icon = get_editor_theme_icon(SNAME("Hsize")); @@ -1723,7 +1726,7 @@ void AnimationTimelineEdit::update_values() { editing = true; if (use_fps && animation->get_step() > 0) { length->set_value(animation->get_length() / animation->get_step()); - length->set_step(1); + length->set_step(FPS_DECIMAL); length->set_tooltip_text(TTR("Animation length (frames)")); time_icon->set_tooltip_text(TTR("Animation length (frames)")); if (track_edit) { @@ -1731,7 +1734,7 @@ void AnimationTimelineEdit::update_values() { } } else { length->set_value(animation->get_length()); - length->set_step(0.0001); + length->set_step(SECOND_DECIMAL); length->set_tooltip_text(TTR("Animation length (seconds)")); time_icon->set_tooltip_text(TTR("Animation length (seconds)")); } @@ -1912,9 +1915,9 @@ AnimationTimelineEdit::AnimationTimelineEdit() { time_icon->set_tooltip_text(TTR("Animation length (seconds)")); len_hb->add_child(time_icon); length = memnew(EditorSpinSlider); - length->set_min(0.0001); + length->set_min(SECOND_DECIMAL); length->set_max(36000); - length->set_step(0.0001); + length->set_step(SECOND_DECIMAL); length->set_allow_greater(true); length->set_custom_minimum_size(Vector2(70 * EDSCALE, 0)); length->set_hide_slider(true); @@ -2645,7 +2648,7 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { } if (key_idx != -1) { - String text = TTR("Time (s):") + " " + TS->format_number(rtos(Math::snapped(animation->track_get_key_time(track, key_idx), 0.0001))) + "\n"; + String text = TTR("Time (s):") + " " + TS->format_number(rtos(Math::snapped(animation->track_get_key_time(track, key_idx), SECOND_DECIMAL))) + "\n"; switch (animation->track_get_type(track)) { case Animation::TYPE_POSITION_3D: { Vector3 t = animation->track_get_key_value(track, key_idx); @@ -4758,10 +4761,12 @@ void AnimationTrackEditor::_animation_changed() { } void AnimationTrackEditor::_snap_mode_changed(int p_mode) { - timeline->set_use_fps(p_mode == 1); + bool use_fps = p_mode == 1; + timeline->set_use_fps(use_fps); if (key_edit) { - key_edit->set_use_fps(p_mode == 1); + key_edit->set_use_fps(use_fps); } + step->set_step(use_fps ? FPS_DECIMAL : SECOND_DECIMAL); _update_step_spinbox(); } @@ -4775,7 +4780,9 @@ void AnimationTrackEditor::_update_step_spinbox() { if (animation->get_step() == 0) { step->set_value(0); } else { - step->set_value(1.0 / animation->get_step()); + // The value stored within tscn cannot restored the original FPS due to lack of precision, + // so the value should be limited to integer. + step->set_value(Math::round(1.0 / animation->get_step())); } } else { @@ -4783,6 +4790,7 @@ void AnimationTrackEditor::_update_step_spinbox() { } step->set_block_signals(false); + _update_snap_unit(); } void AnimationTrackEditor::_animation_update() { @@ -4876,6 +4884,8 @@ void AnimationTrackEditor::_update_step(double p_new_step) { return; } + _update_snap_unit(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Change Animation Step")); float step_value = p_new_step; @@ -5162,7 +5172,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { p_ofs = snap_time(p_ofs); } while (animation->track_find_key(p_track, p_ofs, Animation::FIND_MODE_APPROX) != -1) { // Make sure insertion point is valid. - p_ofs += 0.0001; + p_ofs += SECOND_DECIMAL; } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); @@ -7007,25 +7017,31 @@ void AnimationTrackEditor::_selection_changed() { } } +void AnimationTrackEditor::_update_snap_unit() { + if (step->get_value() <= 0) { + snap_unit = 0; + return; // Avoid zero div. + } + + if (timeline->is_using_fps()) { + snap_unit = 1.0 / step->get_value(); + } else { + snap_unit = 1.0 / Math::round(1.0 / step->get_value()); // Follow the snap behavior of the timeline editor. + } +} + float AnimationTrackEditor::snap_time(float p_value, bool p_relative) { if (is_snap_enabled()) { - double snap_increment; - if (timeline->is_using_fps() && step->get_value() > 0) { - snap_increment = 1.0 / step->get_value(); - } else { - snap_increment = step->get_value(); - } - if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) { // Use more precise snapping when holding Shift. - snap_increment *= 0.25; + snap_unit *= 0.25; } if (p_relative) { - double rel = Math::fmod(timeline->get_value(), snap_increment); - p_value = Math::snapped(p_value + rel, snap_increment) - rel; + double rel = Math::fmod(timeline->get_value(), snap_unit); + p_value = Math::snapped(p_value + rel, snap_unit) - rel; } else { - p_value = Math::snapped(p_value, snap_increment); + p_value = Math::snapped(p_value, snap_unit); } } @@ -7282,7 +7298,7 @@ AnimationTrackEditor::AnimationTrackEditor() { step = memnew(EditorSpinSlider); step->set_min(0); step->set_max(1000000); - step->set_step(0.0001); + step->set_step(SECOND_DECIMAL); step->set_hide_slider(true); step->set_custom_minimum_size(Size2(100, 0) * EDSCALE); step->set_tooltip_text(TTR("Animation step value.")); @@ -7524,9 +7540,9 @@ AnimationTrackEditor::AnimationTrackEditor() { ease_selection->select(Tween::EASE_IN_OUT); // Default ease_selection->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Translation context is needed. ease_fps = memnew(SpinBox); - ease_fps->set_min(1); + ease_fps->set_min(FPS_DECIMAL); ease_fps->set_max(999); - ease_fps->set_step(1); + ease_fps->set_step(FPS_DECIMAL); ease_fps->set_value(30); // Default ease_grid->add_child(memnew(Label(TTR("Transition Type:")))); ease_grid->add_child(transition_selection); @@ -7550,9 +7566,9 @@ AnimationTrackEditor::AnimationTrackEditor() { bake_value = memnew(CheckBox); bake_value->set_pressed(true); bake_fps = memnew(SpinBox); - bake_fps->set_min(1); + bake_fps->set_min(FPS_DECIMAL); bake_fps->set_max(999); - bake_fps->set_step(1); + bake_fps->set_step(FPS_DECIMAL); bake_fps->set_value(30); // Default bake_grid->add_child(memnew(Label(TTR("3D Pos/Rot/Scl Track:")))); bake_grid->add_child(bake_trs); @@ -7671,15 +7687,14 @@ AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animat spinner->set_allow_lesser(true); if (use_fps) { - spinner->set_step(1); - spinner->set_hide_slider(true); + spinner->set_step(FPS_DECIMAL); real_t fps = animation->get_step(); if (fps > 0) { fps = 1.0 / fps; } spinner->set_value(key_ofs * fps); } else { - spinner->set_step(0.0001); + spinner->set_step(SECOND_DECIMAL); spinner->set_value(key_ofs); spinner->set_max(animation->get_length()); } diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index f449b51b81..09f751c8dd 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -649,6 +649,9 @@ class AnimationTrackEditor : public VBoxContainer { void _pick_track_select_recursive(TreeItem *p_item, const String &p_filter, Vector<Node *> &p_select_candidates); void _pick_track_filter_input(const Ref<InputEvent> &p_ie); + double snap_unit; + void _update_snap_unit(); + protected: static void _bind_methods(); void _notification(int p_what); diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 1145a10f71..cede2c0ab6 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -294,6 +294,13 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me } for (const MethodInfo &mi : p_methods) { + if (mi.name.begins_with("@")) { + // GH-92782. GDScript inline setters/getters are historically present in `get_method_list()` + // and can be called using `Object.call()`. However, these functions are meant to be internal + // and their names are not valid identifiers, so let's hide them from the user. + continue; + } + if (!p_search_string.is_empty() && !mi.name.containsn(p_search_string)) { continue; } @@ -324,8 +331,10 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me continue; } } + ret.push_back(mi); } + return ret; } diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 3adff84e40..37e00bf042 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1714,105 +1714,112 @@ HashSet<StringName> EditorFileSystem::_get_scene_groups(const String &p_path) { void EditorFileSystem::update_file(const String &p_file) { ERR_FAIL_COND(p_file.is_empty()); - EditorFileSystemDirectory *fs = nullptr; - int cpos = -1; + update_files({ p_file }); +} - if (!_find_file(p_file, &fs, cpos)) { - if (!fs) { - return; +void EditorFileSystem::update_files(const Vector<String> &p_script_paths) { + for (const String &file : p_script_paths) { + ERR_CONTINUE(file.is_empty()); + EditorFileSystemDirectory *fs = nullptr; + int cpos = -1; + + if (!_find_file(file, &fs, cpos)) { + if (!fs) { + return; + } } - } - if (!FileAccess::exists(p_file)) { - //was removed - _delete_internal_files(p_file); - if (cpos != -1) { // Might've never been part of the editor file system (*.* files deleted in Open dialog). - if (fs->files[cpos]->uid != ResourceUID::INVALID_ID) { - if (ResourceUID::get_singleton()->has_id(fs->files[cpos]->uid)) { - ResourceUID::get_singleton()->remove_id(fs->files[cpos]->uid); + if (!FileAccess::exists(file)) { + //was removed + _delete_internal_files(file); + if (cpos != -1) { // Might've never been part of the editor file system (*.* files deleted in Open dialog). + if (fs->files[cpos]->uid != ResourceUID::INVALID_ID) { + if (ResourceUID::get_singleton()->has_id(fs->files[cpos]->uid)) { + ResourceUID::get_singleton()->remove_id(fs->files[cpos]->uid); + } } - } - if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) { - _queue_update_script_class(p_file); - } - if (fs->files[cpos]->type == SNAME("PackedScene")) { - _queue_update_scene_groups(p_file); + if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) { + _queue_update_script_class(file); + } + if (fs->files[cpos]->type == SNAME("PackedScene")) { + _queue_update_scene_groups(file); + } + + memdelete(fs->files[cpos]); + fs->files.remove_at(cpos); } - memdelete(fs->files[cpos]); - fs->files.remove_at(cpos); + _update_pending_script_classes(); + _update_pending_scene_groups(); + call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later + return; } - _update_pending_script_classes(); - _update_pending_scene_groups(); - call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later - return; - } - - String type = ResourceLoader::get_resource_type(p_file); - if (type.is_empty() && textfile_extensions.has(p_file.get_extension())) { - type = "TextFile"; - } - String script_class = ResourceLoader::get_resource_script_class(p_file); + String type = ResourceLoader::get_resource_type(file); + if (type.is_empty() && textfile_extensions.has(file.get_extension())) { + type = "TextFile"; + } + String script_class = ResourceLoader::get_resource_script_class(file); - ResourceUID::ID uid = ResourceLoader::get_resource_uid(p_file); + ResourceUID::ID uid = ResourceLoader::get_resource_uid(file); - if (cpos == -1) { - // The file did not exist, it was added. - int idx = 0; - String file_name = p_file.get_file(); + if (cpos == -1) { + // The file did not exist, it was added. + int idx = 0; + String file_name = file.get_file(); - for (int i = 0; i < fs->files.size(); i++) { - if (p_file.filenocasecmp_to(fs->files[i]->file) < 0) { - break; + for (int i = 0; i < fs->files.size(); i++) { + if (file.filenocasecmp_to(fs->files[i]->file) < 0) { + break; + } + idx++; } - idx++; - } - EditorFileSystemDirectory::FileInfo *fi = memnew(EditorFileSystemDirectory::FileInfo); - fi->file = file_name; - fi->import_modified_time = 0; - fi->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(p_file); + EditorFileSystemDirectory::FileInfo *fi = memnew(EditorFileSystemDirectory::FileInfo); + fi->file = file_name; + fi->import_modified_time = 0; + fi->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(file); - if (idx == fs->files.size()) { - fs->files.push_back(fi); + if (idx == fs->files.size()) { + fs->files.push_back(fi); + } else { + fs->files.insert(idx, fi); + } + cpos = idx; } else { - fs->files.insert(idx, fi); + //the file exists and it was updated, and was not added in this step. + //this means we must force upon next restart to scan it again, to get proper type and dependencies + late_update_files.insert(file); + _save_late_updated_files(); //files need to be updated in the re-scan } - cpos = idx; - } else { - //the file exists and it was updated, and was not added in this step. - //this means we must force upon next restart to scan it again, to get proper type and dependencies - late_update_files.insert(p_file); - _save_late_updated_files(); //files need to be updated in the re-scan - } - fs->files[cpos]->type = type; - fs->files[cpos]->resource_script_class = script_class; - fs->files[cpos]->uid = uid; - fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path); - fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(p_file); - fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file); - fs->files[cpos]->deps = _get_dependencies(p_file); - fs->files[cpos]->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(p_file); + fs->files[cpos]->type = type; + fs->files[cpos]->resource_script_class = script_class; + fs->files[cpos]->uid = uid; + fs->files[cpos]->script_class_name = _get_global_script_class(type, file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path); + fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(file); + fs->files[cpos]->modified_time = FileAccess::get_modified_time(file); + fs->files[cpos]->deps = _get_dependencies(file); + fs->files[cpos]->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(file); - if (uid != ResourceUID::INVALID_ID) { - if (ResourceUID::get_singleton()->has_id(uid)) { - ResourceUID::get_singleton()->set_id(uid, p_file); - } else { - ResourceUID::get_singleton()->add_id(uid, p_file); - } + if (uid != ResourceUID::INVALID_ID) { + if (ResourceUID::get_singleton()->has_id(uid)) { + ResourceUID::get_singleton()->set_id(uid, file); + } else { + ResourceUID::get_singleton()->add_id(uid, file); + } - ResourceUID::get_singleton()->update_cache(); - } - // Update preview - EditorResourcePreview::get_singleton()->check_for_invalidation(p_file); + ResourceUID::get_singleton()->update_cache(); + } + // Update preview + EditorResourcePreview::get_singleton()->check_for_invalidation(file); - if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) { - _queue_update_script_class(p_file); - } - if (fs->files[cpos]->type == SNAME("PackedScene")) { - _queue_update_scene_groups(p_file); + if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) { + _queue_update_script_class(file); + } + if (fs->files[cpos]->type == SNAME("PackedScene")) { + _queue_update_scene_groups(file); + } } _update_pending_script_classes(); diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 84ae1e182c..cd95d5fb95 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -310,6 +310,7 @@ public: void scan(); void scan_changes(); void update_file(const String &p_file); + void update_files(const Vector<String> &p_script_paths); HashSet<String> get_valid_extensions() const; void register_global_class_script(const String &p_search_path, const String &p_target_path); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 80e2302e91..33f460e23d 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -652,6 +652,19 @@ void EditorProperty::add_focusable(Control *p_control) { focusables.push_back(p_control); } +void EditorProperty::grab_focus(int p_focusable) { + if (focusables.is_empty()) { + return; + } + + if (p_focusable >= 0) { + ERR_FAIL_INDEX(p_focusable, focusables.size()); + focusables[p_focusable]->grab_focus(); + } else { + focusables[0]->grab_focus(); + } +} + void EditorProperty::select(int p_focusable) { bool already_selected = selected; if (!selectable) { diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index a0ced55bd8..f9b0d1f094 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -192,6 +192,7 @@ public: void set_deletable(bool p_enable); bool is_deletable() const; void add_focusable(Control *p_control); + void grab_focus(int p_focusable = -1); void select(int p_focusable = -1); void deselect(); bool is_selected() const; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index dd1274e9ee..94bd590fc1 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3706,12 +3706,15 @@ void EditorNode::_remove_scene(int index, bool p_change_tab) { } void EditorNode::set_edited_scene(Node *p_scene) { + set_edited_scene_root(p_scene, true); +} + +void EditorNode::set_edited_scene_root(Node *p_scene, bool p_auto_add) { Node *old_edited_scene_root = get_editor_data().get_edited_scene_root(); - if (old_edited_scene_root) { - if (old_edited_scene_root->get_parent() == scene_root) { - scene_root->remove_child(old_edited_scene_root); - } - old_edited_scene_root->disconnect(SNAME("replacing_by"), callable_mp(this, &EditorNode::set_edited_scene)); + ERR_FAIL_COND_MSG(p_scene && p_scene != old_edited_scene_root && p_scene->get_parent(), "Non-null nodes that are set as edited scene should not have a parent node."); + + if (p_auto_add && old_edited_scene_root && old_edited_scene_root->get_parent() == scene_root) { + scene_root->remove_child(old_edited_scene_root); } get_editor_data().set_edited_scene_root(p_scene); @@ -3723,11 +3726,8 @@ void EditorNode::set_edited_scene(Node *p_scene) { get_tree()->set_edited_scene_root(p_scene); } - if (p_scene) { - if (p_scene->get_parent() != scene_root) { - scene_root->add_child(p_scene, true); - } - p_scene->connect(SNAME("replacing_by"), callable_mp(this, &EditorNode::set_edited_scene)); + if (p_auto_add && p_scene) { + scene_root->add_child(p_scene, true); } } @@ -5967,6 +5967,8 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins instantiated_node->set_scene_file_path(String()); } current_edited_scene = instantiated_node; + + editor_data.set_edited_scene_root(current_edited_scene); } // Replace the original node with the instantiated version. diff --git a/editor/editor_node.h b/editor/editor_node.h index 5d7bd5b4f8..899da99450 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -783,6 +783,7 @@ public: SubViewport *get_scene_root() { return scene_root; } // Root of the scene being edited. void set_edited_scene(Node *p_scene); + void set_edited_scene_root(Node *p_scene, bool p_auto_add); Node *get_edited_scene() { return editor_data.get_edited_scene_root(); } void fix_dependencies(const String &p_for_file); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 555165c156..53a10a779f 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -255,6 +255,10 @@ void EditorPropertyArray::_change_type_menu(int p_index) { return; } + ERR_FAIL_COND_MSG( + changing_type_index == EditorPropertyArrayObject::NOT_CHANGING_TYPE, + "Tried to change type of an array item, but no item was selected."); + Variant value; VariantInternal::initialize(&value, Variant::Type(p_index)); @@ -444,6 +448,10 @@ void EditorPropertyArray::update_property() { slot.prop = new_prop; slot.set_index(idx); } + if (slot.index == changing_type_index) { + callable_mp(slot.prop, &EditorProperty::grab_focus).call_deferred(0); + changing_type_index = EditorPropertyArrayObject::NOT_CHANGING_TYPE; + } slot.prop->update_property(); } @@ -921,6 +929,10 @@ void EditorPropertyDictionary::_create_new_property_slot(int p_idx) { } void EditorPropertyDictionary::_change_type_menu(int p_index) { + ERR_FAIL_COND_MSG( + changing_type_index == EditorPropertyDictionaryObject::NOT_CHANGING_TYPE, + "Tried to change the type of a dict key or value, but nothing was selected."); + Variant value; switch (changing_type_index) { case EditorPropertyDictionaryObject::NEW_KEY_INDEX: @@ -1062,6 +1074,14 @@ void EditorPropertyDictionary::update_property() { new_prop->set_read_only(is_read_only()); slot.set_prop(new_prop); } + + // We need to grab the focus of the property that is being changed, even if the type didn't actually changed. + // Otherwise, focus will stay on the change type button, which is not very user friendly. + if (changing_type_index == slot.index) { + callable_mp(slot.prop, &EditorProperty::grab_focus).call_deferred(0); + changing_type_index = EditorPropertyDictionaryObject::NOT_CHANGING_TYPE; // Reset to avoid grabbing focus again. + } + slot.prop->update_property(); } updating = false; diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h index 8b939ab0b0..267cb1e86d 100644 --- a/editor/editor_properties_array_dict.h +++ b/editor/editor_properties_array_dict.h @@ -49,6 +49,10 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; public: + enum { + NOT_CHANGING_TYPE = -1, + }; + void set_array(const Variant &p_array); Variant get_array(); @@ -68,7 +72,8 @@ protected: public: enum { - NEW_KEY_INDEX = -2, + NOT_CHANGING_TYPE = -3, + NEW_KEY_INDEX, NEW_VALUE_INDEX, }; @@ -111,7 +116,7 @@ class EditorPropertyArray : public EditorProperty { int page_length = 20; int page_index = 0; - int changing_type_index; + int changing_type_index = EditorPropertyArrayObject::NOT_CHANGING_TYPE; Button *edit = nullptr; PanelContainer *container = nullptr; VBoxContainer *property_vbox = nullptr; @@ -206,7 +211,7 @@ class EditorPropertyDictionary : public EditorProperty { Ref<EditorPropertyDictionaryObject> object; int page_length = 20; int page_index = 0; - int changing_type_index; + int changing_type_index = EditorPropertyDictionaryObject::NOT_CHANGING_TYPE; Button *edit = nullptr; PanelContainer *container = nullptr; VBoxContainer *property_vbox = nullptr; diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index dd698d74b6..742d29fef0 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -138,7 +138,6 @@ void EditorResourcePreview::_preview_ready(const String &p_path, int p_hash, con } Item item; - item.order = order++; item.preview = p_texture; item.small_preview = p_small_texture; item.last_hash = p_hash; @@ -412,7 +411,6 @@ void EditorResourcePreview::queue_edited_resource_preview(const Ref<Resource> &p String path_id = "ID:" + itos(p_res->get_instance_id()); if (cache.has(path_id) && cache[path_id].last_hash == p_res->hash_edited_version_for_preview()) { - cache[path_id].order = order++; p_receiver->call(p_receiver_func, path_id, cache[path_id].preview, cache[path_id].small_preview, p_userdata); return; } @@ -439,7 +437,6 @@ void EditorResourcePreview::queue_resource_preview(const String &p_path, Object MutexLock lock(preview_mutex); if (cache.has(p_path)) { - cache[p_path].order = order++; p_receiver->call(p_receiver_func, p_path, cache[p_path].preview, cache[p_path].small_preview, p_userdata); return; } @@ -533,7 +530,6 @@ void EditorResourcePreview::stop() { EditorResourcePreview::EditorResourcePreview() { singleton = this; - order = 0; } EditorResourcePreview::~EditorResourcePreview() { diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h index 6b67acceaa..2870f9a201 100644 --- a/editor/editor_resource_preview.h +++ b/editor/editor_resource_preview.h @@ -99,13 +99,10 @@ class EditorResourcePreview : public Node { Ref<Texture2D> preview; Ref<Texture2D> small_preview; Dictionary preview_metadata; - int order = 0; uint32_t last_hash = 0; uint64_t modified_time = 0; }; - int order; - HashMap<String, Item> cache; void _preview_ready(const String &p_path, int p_hash, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud, const Dictionary &p_metadata); diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp index 2622645d7c..4862b3436e 100644 --- a/editor/gui/editor_scene_tabs.cpp +++ b/editor/gui/editor_scene_tabs.cpp @@ -405,7 +405,7 @@ EditorSceneTabs::EditorSceneTabs() { scene_tabs->connect(SceneStringName(mouse_exited), callable_mp(this, &EditorSceneTabs::_scene_tab_exit)); scene_tabs->connect(SceneStringName(gui_input), callable_mp(this, &EditorSceneTabs::_scene_tab_input)); scene_tabs->connect("active_tab_rearranged", callable_mp(this, &EditorSceneTabs::_reposition_active_tab)); - scene_tabs->connect(SceneStringName(resized), callable_mp(this, &EditorSceneTabs::_scene_tabs_resized)); + scene_tabs->connect(SceneStringName(resized), callable_mp(this, &EditorSceneTabs::_scene_tabs_resized), CONNECT_DEFERRED); scene_tabs_context_menu = memnew(PopupMenu); tabbar_container->add_child(scene_tabs_context_menu); diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index 7e2d6c9d1e..fc52d7a0ae 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -232,23 +232,27 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { item->set_icon(0, icon); item->set_metadata(0, p_node->get_path()); + if (connecting_signal) { + // Add script icons for all scripted nodes. + Ref<Script> scr = p_node->get_script(); + if (scr.is_valid()) { + item->add_button(0, get_editor_theme_icon(SNAME("Script")), BUTTON_SCRIPT); + if (EditorNode::get_singleton()->get_object_custom_type_base(p_node) == scr) { + // Disable button on custom scripts (pure visual cue). + item->set_button_disabled(0, item->get_button_count(0) - 1, true); + } + } + } + if (connect_to_script_mode) { Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor)); Ref<Script> scr = p_node->get_script(); - if (!scr.is_null() && EditorNode::get_singleton()->get_object_custom_type_base(p_node) != scr) { - //has script - item->add_button(0, get_editor_theme_icon(SNAME("Script")), BUTTON_SCRIPT); - } else { - //has no script (or script is a custom type) + bool has_custom_script = scr.is_valid() && EditorNode::get_singleton()->get_object_custom_type_base(p_node) == scr; + if (scr.is_null() || has_custom_script) { _set_item_custom_color(item, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor))); item->set_selectable(0, false); - if (!scr.is_null()) { // make sure to mark the script if a custom type - item->add_button(0, get_editor_theme_icon(SNAME("Script")), BUTTON_SCRIPT); - item->set_button_disabled(0, item->get_button_count(0) - 1, true); - } - accent.a *= 0.7; } diff --git a/editor/gui/scene_tree_editor.h b/editor/gui/scene_tree_editor.h index 9ae1e99a27..b4d9644f16 100644 --- a/editor/gui/scene_tree_editor.h +++ b/editor/gui/scene_tree_editor.h @@ -160,7 +160,6 @@ public: void set_marked(const HashSet<Node *> &p_marked, bool p_selectable = true, bool p_children_selectable = true); void set_marked(Node *p_marked, bool p_selectable = true, bool p_children_selectable = true); - bool has_marked() const { return !marked.is_empty(); } void set_selected(Node *p_node, bool p_emit_selected = true); Node *get_selected(); void set_can_rename(bool p_can_rename) { can_rename = p_can_rename; } diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 3c06c68414..2347c715a8 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -189,13 +189,15 @@ void ImportDock::_update_options(const String &p_path, const Ref<ConfigFile> &p_ params->base_options_path = p_path; HashMap<StringName, Variant> import_options; - List<String> section_keys; - p_config->get_section_keys("params", §ion_keys); - for (const String §ion_key : section_keys) { - import_options[section_key] = p_config->get_value("params", section_key); - } - if (params->importer.is_valid()) { - params->importer->handle_compatibility_options(import_options); + if (p_config.is_valid() && p_config->has_section("params")) { + List<String> section_keys; + p_config->get_section_keys("params", §ion_keys); + for (const String §ion_key : section_keys) { + import_options[section_key] = p_config->get_value("params", section_key); + } + if (params->importer.is_valid()) { + params->importer->handle_compatibility_options(import_options); + } } for (const ResourceImporter::ImportOption &E : options) { diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index fe56f48889..0a2c192ea4 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -243,12 +243,12 @@ void AnimationPlayerEditor::_play_from_pressed() { String current = _get_current(); if (!current.is_empty()) { - float time = player->get_current_animation_position(); + double 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->clear_caches(); //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->seek(time); + player->seek(time, true, true); player->play(current); } @@ -281,12 +281,12 @@ void AnimationPlayerEditor::_play_bw_from_pressed() { String current = _get_current(); if (!current.is_empty()) { - float time = player->get_current_animation_position(); - if (current == player->get_assigned_animation()) { - player->stop(); //so it won't blend with itself + double time = player->get_current_animation_position(); + if (current == player->get_assigned_animation() && player->is_playing()) { + player->clear_caches(); //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->seek(time); + player->seek(time, true, true); player->play_backwards(current); } diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 202817f6ee..e4eaab7325 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -922,7 +922,7 @@ void vertex() { VERTEX = VERTEX; POSITION = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * vec4(VERTEX.xyz, 1.0); - POSITION.z = mix(POSITION.z, 0.0, 0.999); + POSITION.z = mix(POSITION.z, POSITION.w, 0.999); POINT_SIZE = point_size; } @@ -1201,7 +1201,7 @@ void vertex() { } VERTEX = VERTEX; POSITION = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * vec4(VERTEX.xyz, 1.0); - POSITION.z = mix(POSITION.z, 0, 0.998); + POSITION.z = mix(POSITION.z, POSITION.w, 0.998); } void fragment() { ALBEDO = COLOR.rgb; diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index cf1ad36adc..6a6e2b83ab 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -743,20 +743,24 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { localization_editor->connect("localization_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); tab_container->add_child(localization_editor); + TabContainer *globals_container = memnew(TabContainer); + globals_container->set_name(TTR("Globals")); + tab_container->add_child(globals_container); + autoload_settings = memnew(EditorAutoloadSettings); autoload_settings->set_name(TTR("Autoload")); autoload_settings->connect("autoload_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); - tab_container->add_child(autoload_settings); + globals_container->add_child(autoload_settings); shaders_global_shader_uniforms_editor = memnew(ShaderGlobalsEditor); shaders_global_shader_uniforms_editor->set_name(TTR("Shader Globals")); shaders_global_shader_uniforms_editor->connect("globals_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); - tab_container->add_child(shaders_global_shader_uniforms_editor); + globals_container->add_child(shaders_global_shader_uniforms_editor); group_settings = memnew(GroupSettingsEditor); - group_settings->set_name(TTR("Global Groups")); + group_settings->set_name(TTR("Groups")); group_settings->connect("group_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); - tab_container->add_child(group_settings); + globals_container->add_child(group_settings); plugin_settings = memnew(EditorPluginSettings); plugin_settings->set_name(TTR("Plugins")); diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp index d123d8ef59..a5157bd394 100644 --- a/editor/property_selector.cpp +++ b/editor/property_selector.cpp @@ -225,11 +225,24 @@ void PropertySelector::_update_search() { } else { Ref<Script> script_ref = Object::cast_to<Script>(ObjectDB::get_instance(script)); if (script_ref.is_valid()) { - methods.push_back(MethodInfo("*Script Methods")); if (script_ref->is_built_in()) { script_ref->reload(true); } - script_ref->get_script_method_list(&methods); + + List<MethodInfo> script_methods; + script_ref->get_script_method_list(&script_methods); + + methods.push_back(MethodInfo("*Script Methods")); // TODO: Split by inheritance. + + for (const MethodInfo &mi : script_methods) { + if (mi.name.begins_with("@")) { + // GH-92782. GDScript inline setters/getters are historically present in `get_method_list()` + // and can be called using `Object.call()`. However, these functions are meant to be internal + // and their names are not valid identifiers, so let's hide them from the user. + continue; + } + methods.push_back(mi); + } } StringName base = base_type; diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 2f57dc5610..4448f165d3 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -76,12 +76,15 @@ void SceneTreeDock::_quick_open() { } void SceneTreeDock::_inspect_hovered_node() { - scene_tree->set_selected(node_hovered_now); - scene_tree->set_marked(node_hovered_now); + select_node_hovered_at_end_of_drag = true; + if (tree_item_inspected != nullptr) { + tree_item_inspected->clear_custom_color(0); + } Tree *tree = scene_tree->get_scene_tree(); - TreeItem *item = tree->get_item_at_position(tree->get_local_mouse_position()); + TreeItem *item = tree->get_item_with_metadata(node_hovered_now->get_path()); if (item) { - item->set_as_cursor(0); + tree_item_inspected = item; + tree_item_inspected->set_custom_color(0, get_theme_color(SNAME("accent_color"), EditorStringName(Editor))); } InspectorDock::get_inspector_singleton()->edit(node_hovered_now); InspectorDock::get_inspector_singleton()->propagate_notification(NOTIFICATION_DRAG_BEGIN); // Enable inspector drag preview after it updated. @@ -130,8 +133,8 @@ void SceneTreeDock::input(const Ref<InputEvent> &p_event) { } if (mb->is_released()) { - if (scene_tree->has_marked()) { - scene_tree->set_marked(nullptr); + if (tree_item_inspected != nullptr) { + tree_item_inspected->clear_custom_color(0); } _reset_hovering_timer(); } @@ -1658,6 +1661,16 @@ void SceneTreeDock::_notification(int p_what) { case NOTIFICATION_DRAG_END: { _reset_hovering_timer(); + if (select_node_hovered_at_end_of_drag && !hovered_but_reparenting) { + Node *node_inspected = Object::cast_to<Node>(InspectorDock::get_inspector_singleton()->get_edited_object()); + if (node_inspected) { + editor_selection->clear(); + editor_selection->add_node(node_inspected); + scene_tree->set_selected(node_inspected); + select_node_hovered_at_end_of_drag = false; + } + } + hovered_but_reparenting = false; } break; } } @@ -2185,6 +2198,7 @@ void SceneTreeDock::_node_reparent(NodePath p_path, bool p_keep_global_xform) { ERR_FAIL_NULL(new_parent); List<Node *> selection = editor_selection->get_selected_node_list(); + List<Node *> full_selection = editor_selection->get_full_selected_node_list(); if (selection.is_empty()) { return; // Nothing to reparent. @@ -2197,6 +2211,10 @@ void SceneTreeDock::_node_reparent(NodePath p_path, bool p_keep_global_xform) { } _do_reparent(new_parent, -1, nodes, p_keep_global_xform); + + for (Node *E : full_selection) { + editor_selection->add_node(E); + } } void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, Vector<Node *> p_nodes, bool p_keep_global_xform) { @@ -2238,6 +2256,9 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V return; // Position and parent didn't change. } + // Prevent selecting the hovered node and keep the reparented node(s) selected instead. + hovered_but_reparenting = true; + Node *validate = new_parent; while (validate) { ERR_FAIL_COND_MSG(p_nodes.has(validate), "Selection changed at some point. Can't reparent."); @@ -2992,6 +3013,10 @@ void SceneTreeDock::_replace_node(Node *p_node, Node *p_by_node, bool p_keep_pro to_erase.push_back(oldnode->get_child(i)); } } + + if (oldnode == edited_scene) { + EditorNode::get_singleton()->set_edited_scene_root(newnode, false); + } oldnode->replace_by(newnode, true); // Re-apply size of anchored control. @@ -3396,6 +3421,7 @@ void SceneTreeDock::_nodes_dragged(const Array &p_nodes, NodePath p_to, int p_ty } List<Node *> selection = editor_selection->get_selected_node_list(); + List<Node *> full_selection = editor_selection->get_full_selected_node_list(); if (selection.is_empty()) { return; //nothing to reparent @@ -3415,7 +3441,8 @@ void SceneTreeDock::_nodes_dragged(const Array &p_nodes, NodePath p_to, int p_ty _normalize_drop(to_node, to_pos, p_type); _do_reparent(to_node, to_pos, nodes, !Input::get_singleton()->is_key_pressed(Key::SHIFT)); - for (Node *E : nodes) { + + for (Node *E : full_selection) { editor_selection->add_node(E); } } diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index abef990995..5028cd5cc9 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -239,8 +239,11 @@ class SceneTreeDock : public VBoxContainer { void _inspect_hovered_node(); void _reset_hovering_timer(); Timer *inspect_hovered_node_delay = nullptr; + TreeItem *tree_item_inspected = nullptr; Node *node_hovered_now = nullptr; Node *node_hovered_previously = nullptr; + bool select_node_hovered_at_end_of_drag = false; + bool hovered_but_reparenting = false; virtual void input(const Ref<InputEvent> &p_event) override; virtual void shortcut_input(const Ref<InputEvent> &p_event) override; |