summaryrefslogtreecommitdiffstats
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/animation_track_editor.cpp75
-rw-r--r--editor/animation_track_editor.h3
-rw-r--r--editor/connections_dialog.cpp9
-rw-r--r--editor/editor_file_system.cpp165
-rw-r--r--editor/editor_file_system.h1
-rw-r--r--editor/editor_inspector.cpp13
-rw-r--r--editor/editor_inspector.h1
-rw-r--r--editor/editor_node.cpp22
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_properties_array_dict.cpp20
-rw-r--r--editor/editor_properties_array_dict.h11
-rw-r--r--editor/editor_resource_preview.cpp4
-rw-r--r--editor/editor_resource_preview.h3
-rw-r--r--editor/gui/editor_scene_tabs.cpp2
-rw-r--r--editor/gui/scene_tree_editor.cpp24
-rw-r--r--editor/gui/scene_tree_editor.h1
-rw-r--r--editor/import_dock.cpp16
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp14
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp4
-rw-r--r--editor/project_settings_editor.cpp12
-rw-r--r--editor/property_selector.cpp17
-rw-r--r--editor/scene_tree_dock.cpp41
-rw-r--r--editor/scene_tree_dock.h3
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", &section_keys);
- for (const String &section_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", &section_keys);
+ for (const String &section_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;