summaryrefslogtreecommitdiffstats
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/SCsub5
-rw-r--r--editor/action_map_editor.cpp6
-rw-r--r--editor/action_map_editor.h3
-rw-r--r--editor/animation_track_editor.cpp6
-rw-r--r--editor/debugger/editor_debugger_node.cpp8
-rw-r--r--editor/debugger/editor_debugger_tree.cpp9
-rw-r--r--editor/debugger/editor_debugger_tree.h1
-rw-r--r--editor/debugger/editor_profiler.cpp14
-rw-r--r--editor/debugger/editor_profiler.h5
-rw-r--r--editor/doc_tools.cpp19
-rw-r--r--editor/editor_builders.py1
-rw-r--r--editor/editor_feature_profile.cpp20
-rw-r--r--editor/editor_feature_profile.h3
-rw-r--r--editor/editor_help.cpp4
-rw-r--r--editor/editor_inspector.compat.inc41
-rw-r--r--editor/editor_inspector.cpp15
-rw-r--r--editor/editor_inspector.h8
-rw-r--r--editor/editor_log.cpp6
-rw-r--r--editor/editor_node.cpp20
-rw-r--r--editor/editor_properties.cpp5
-rw-r--r--editor/editor_properties.h1
-rw-r--r--editor/editor_properties_array_dict.cpp1
-rw-r--r--editor/editor_settings_dialog.cpp13
-rw-r--r--editor/editor_settings_dialog.h14
-rw-r--r--editor/export/project_export.cpp24
-rw-r--r--editor/filesystem_dock.cpp18
-rw-r--r--editor/filesystem_dock.h1
-rw-r--r--editor/gui/editor_toaster.cpp2
-rw-r--r--editor/gui/scene_tree_editor.cpp23
-rw-r--r--editor/icons/AudioStreamPlayer.svg2
-rw-r--r--editor/icons/AudioStreamPlayer2D.svg2
-rw-r--r--editor/icons/AudioStreamPlayer3D.svg2
-rw-r--r--editor/icons/SCsub2
-rw-r--r--editor/import/3d/resource_importer_scene.cpp5
-rw-r--r--editor/import/resource_importer_shader_file.cpp2
-rw-r--r--editor/import/resource_importer_texture.cpp6
-rw-r--r--editor/import_dock.cpp14
-rw-r--r--editor/input_event_configuration_dialog.cpp6
-rw-r--r--editor/input_event_configuration_dialog.h1
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp376
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h20
-rw-r--r--editor/plugins/cpu_particles_2d_editor_plugin.cpp3
-rw-r--r--editor/plugins/cpu_particles_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp3
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp275
-rw-r--r--editor/plugins/node_3d_editor_plugin.h9
-rw-r--r--editor/plugins/script_editor_plugin.cpp43
-rw-r--r--editor/plugins/script_text_editor.cpp11
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp8
-rw-r--r--editor/plugins/text_editor.cpp6
-rw-r--r--editor/plugins/text_shader_editor.cpp15
-rw-r--r--editor/plugins/text_shader_editor.h2
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp42
-rw-r--r--editor/plugins/tiles/tile_atlas_view.h4
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp19
-rw-r--r--editor/plugins/tiles/tile_map_layer_editor.cpp31
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp16
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp13
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp3
-rw-r--r--editor/project_manager/project_list.cpp59
-rw-r--r--editor/project_manager/project_list.h7
-rw-r--r--editor/project_settings_editor.cpp2
-rw-r--r--editor/rename_dialog.cpp1
-rw-r--r--editor/scene_tree_dock.cpp136
-rw-r--r--editor/scene_tree_dock.h3
-rw-r--r--editor/themes/SCsub2
68 files changed, 1033 insertions, 422 deletions
diff --git a/editor/SCsub b/editor/SCsub
index e613a71238..029048969a 100644
--- a/editor/SCsub
+++ b/editor/SCsub
@@ -4,11 +4,12 @@ Import("env")
env.editor_sources = []
-import os
import glob
+import os
+
import editor_builders
-import methods
+import methods
if env.editor_build:
# Generate doc data paths
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp
index 6ff0520ab6..3023c5907a 100644
--- a/editor/action_map_editor.cpp
+++ b/editor/action_map_editor.cpp
@@ -505,6 +505,9 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
event_item->set_button_color(2, 1, Color(1, 1, 1, 0.75));
}
}
+
+ // Update UI.
+ clear_all_search->set_disabled(action_list_search->get_text().is_empty() && action_list_search_by_event->get_event().is_null());
}
void ActionMapEditor::show_message(const String &p_message) {
@@ -550,8 +553,9 @@ ActionMapEditor::ActionMapEditor() {
action_list_search_by_event->connect(SceneStringName(focus_exited), callable_mp(this, &ActionMapEditor::_on_filter_unfocused));
top_hbox->add_child(action_list_search_by_event);
- Button *clear_all_search = memnew(Button);
+ clear_all_search = memnew(Button);
clear_all_search->set_text(TTR("Clear All"));
+ clear_all_search->set_tooltip_text(TTR("Clear all search filters."));
clear_all_search->connect(SceneStringName(pressed), callable_mp(action_list_search_by_event, &EventListenerLineEdit::clear_event));
clear_all_search->connect(SceneStringName(pressed), callable_mp(action_list_search, &LineEdit::clear));
top_hbox->add_child(clear_all_search);
diff --git a/editor/action_map_editor.h b/editor/action_map_editor.h
index 2b329f2fca..017296bfaa 100644
--- a/editor/action_map_editor.h
+++ b/editor/action_map_editor.h
@@ -52,7 +52,7 @@ public:
bool has_initial = false;
Dictionary action_initial;
- Ref<Texture2D> icon = Ref<Texture2D>();
+ Ref<Texture2D> icon;
bool editable = true;
};
@@ -85,6 +85,7 @@ private:
CheckButton *show_builtin_actions_checkbutton = nullptr;
LineEdit *action_list_search = nullptr;
EventListenerLineEdit *action_list_search_by_event = nullptr;
+ Button *clear_all_search = nullptr;
HBoxContainer *add_hbox = nullptr;
LineEdit *add_edit = nullptr;
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index ebb63dd57c..045774080c 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -7312,17 +7312,19 @@ AnimationTrackEditor::AnimationTrackEditor() {
bottom_hb->add_child(zoom);
timeline->set_zoom(zoom);
+ ED_SHORTCUT("animation_editor/auto_fit", TTR("Fit to panel"), KeyModifierMask::ALT | Key::F);
+
auto_fit = memnew(Button);
auto_fit->set_flat(true);
auto_fit->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_auto_fit));
- auto_fit->set_shortcut(ED_SHORTCUT("animation_editor/auto_fit", TTR("Fit to panel"), KeyModifierMask::ALT | Key::F));
+ auto_fit->set_shortcut(ED_GET_SHORTCUT("animation_editor/auto_fit"));
bottom_hb->add_child(auto_fit);
auto_fit_bezier = memnew(Button);
auto_fit_bezier->set_flat(true);
auto_fit_bezier->set_visible(false);
auto_fit_bezier->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_auto_fit_bezier));
- auto_fit_bezier->set_shortcut(ED_SHORTCUT("animation_editor/auto_fit", TTR("Fit to panel"), KeyModifierMask::ALT | Key::F));
+ auto_fit_bezier->set_shortcut(ED_GET_SHORTCUT("animation_editor/auto_fit"));
bottom_hb->add_child(auto_fit_bezier);
edit = memnew(MenuButton);
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 1d3c7aec3f..2f7183b883 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -314,12 +314,18 @@ void EditorDebuggerNode::stop(bool p_force) {
void EditorDebuggerNode::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- if (tabs->get_tab_count() > 1 && EditorThemeManager::is_generated_theme_outdated()) {
+ if (!EditorThemeManager::is_generated_theme_outdated()) {
+ return;
+ }
+
+ if (tabs->get_tab_count() > 1) {
add_theme_constant_override("margin_left", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_LEFT));
add_theme_constant_override("margin_right", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_RIGHT));
tabs->add_theme_style_override("panel", EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles)));
}
+
+ remote_scene_tree->update_icon_max_width();
} break;
case NOTIFICATION_READY: {
diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp
index 63053d2574..12b590da3c 100644
--- a/editor/debugger/editor_debugger_tree.cpp
+++ b/editor/debugger/editor_debugger_tree.cpp
@@ -31,6 +31,7 @@
#include "editor_debugger_tree.h"
#include "editor/editor_node.h"
+#include "editor/editor_string_names.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/scene_tree_dock.h"
#include "scene/debugger/scene_debugger.h"
@@ -62,6 +63,10 @@ void EditorDebuggerTree::_notification(int p_what) {
connect("item_collapsed", callable_mp(this, &EditorDebuggerTree::_scene_tree_folded));
connect("item_mouse_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_rmb_selected));
} break;
+
+ case NOTIFICATION_ENTER_TREE: {
+ update_icon_max_width();
+ } break;
}
}
@@ -293,6 +298,10 @@ Variant EditorDebuggerTree::get_drag_data(const Point2 &p_point) {
return vformat("\"%s\"", path);
}
+void EditorDebuggerTree::update_icon_max_width() {
+ add_theme_constant_override("icon_max_width", get_theme_constant("class_icon_size", EditorStringName(Editor)));
+}
+
String EditorDebuggerTree::get_selected_path() {
if (!get_selected()) {
return "";
diff --git a/editor/debugger/editor_debugger_tree.h b/editor/debugger/editor_debugger_tree.h
index 895f33f1a2..dbffb0f219 100644
--- a/editor/debugger/editor_debugger_tree.h
+++ b/editor/debugger/editor_debugger_tree.h
@@ -72,6 +72,7 @@ public:
virtual Variant get_drag_data(const Point2 &p_point) override;
+ void update_icon_max_width();
String get_selected_path();
ObjectID get_selected_object();
int get_current_debugger(); // Would love to have one tree for every debugger.
diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp
index 69cf13ea0b..0e2a7ee599 100644
--- a/editor/debugger/editor_profiler.cpp
+++ b/editor/debugger/editor_profiler.cpp
@@ -34,6 +34,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/themes/editor_scale.h"
+#include "editor/themes/editor_theme_manager.h"
#include "scene/resources/image_texture.h"
void EditorProfiler::_make_metric_ptrs(Metric &m) {
@@ -423,6 +424,15 @@ void EditorProfiler::_notification(int p_what) {
case NOTIFICATION_TRANSLATION_CHANGED: {
activate->set_icon(get_editor_theme_icon(SNAME("Play")));
clear_button->set_icon(get_editor_theme_icon(SNAME("Clear")));
+
+ theme_cache.seek_line_color = get_theme_color(SNAME("font_color"), EditorStringName(Editor));
+ theme_cache.seek_line_color.a = 0.8;
+ theme_cache.seek_line_hover_color = theme_cache.seek_line_color;
+ theme_cache.seek_line_hover_color.a = 0.4;
+
+ if (total_metrics > 0) {
+ _update_plot();
+ }
} break;
}
}
@@ -434,11 +444,11 @@ void EditorProfiler::_graph_tex_draw() {
if (seeking) {
int frame = cursor_metric_edit->get_value() - _get_frame_metric(0).frame_number;
int cur_x = (2 * frame + 1) * graph->get_size().x / (2 * frame_metrics.size()) + 1;
- graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), Color(1, 1, 1, 0.8));
+ graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), theme_cache.seek_line_color);
}
if (hover_metric > -1 && hover_metric < total_metrics) {
int cur_x = (2 * hover_metric + 1) * graph->get_size().x / (2 * frame_metrics.size()) + 1;
- graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), Color(1, 1, 1, 0.4));
+ graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), theme_cache.seek_line_hover_color);
}
}
diff --git a/editor/debugger/editor_profiler.h b/editor/debugger/editor_profiler.h
index 620d21fe98..64253070b1 100644
--- a/editor/debugger/editor_profiler.h
+++ b/editor/debugger/editor_profiler.h
@@ -94,6 +94,11 @@ public:
};
private:
+ struct ThemeCache {
+ Color seek_line_color;
+ Color seek_line_hover_color;
+ } theme_cache;
+
Button *activate = nullptr;
Button *clear_button = nullptr;
TextureRect *graph = nullptr;
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index b58e82b7e7..331dacf6ad 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -344,25 +344,6 @@ void DocTools::merge_from(const DocTools &p_data) {
merge_theme_properties(c.theme_properties, cf.theme_properties);
merge_operators(c.operators, cf.operators);
-
-#ifndef MODULE_MONO_ENABLED
- // The Mono module defines some properties that we want to keep when
- // re-generating docs with a non-Mono build, to prevent pointless diffs
- // (and loss of descriptions) depending on the config of the doc writer.
- // We use a horrible hack to force keeping the relevant properties,
- // hardcoded below. At least it's an ad hoc hack... ¯\_(ツ)_/¯
- // Don't show this to your kids.
- if (c.name == "@GlobalScope") {
- // Retrieve GodotSharp singleton.
- for (int j = 0; j < cf.properties.size(); j++) {
- if (cf.properties[j].name == "GodotSharp") {
- c.properties.push_back(cf.properties[j]);
- c.properties.sort();
- break;
- }
- }
- }
-#endif
}
}
diff --git a/editor/editor_builders.py b/editor/editor_builders.py
index cfe6e56b49..625d570666 100644
--- a/editor/editor_builders.py
+++ b/editor/editor_builders.py
@@ -7,6 +7,7 @@ import subprocess
import tempfile
import uuid
import zlib
+
from methods import print_warning
diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp
index 5a7e914c11..020706c714 100644
--- a/editor/editor_feature_profile.cpp
+++ b/editor/editor_feature_profile.cpp
@@ -497,8 +497,8 @@ void EditorFeatureProfileManager::_hide_requested() {
_cancel_pressed(); // From AcceptDialog.
}
-void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected) {
- TreeItem *class_item = class_list->create_item(p_parent);
+void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected, int p_class_insert_index) {
+ TreeItem *class_item = class_list->create_item(p_parent, p_class_insert_index);
class_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
class_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_class));
String text = p_class;
@@ -647,7 +647,7 @@ void EditorFeatureProfileManager::_class_list_item_edited() {
String class_selected = md;
edited->set_disable_class(class_selected, !checked);
_save_and_update();
- _update_selected_profile();
+ _update_profile_tree_from(item);
} else if (md.get_type() == Variant::INT) {
int feature_selected = md;
edited->set_disable_feature(EditorFeatureProfile::Feature(feature_selected), !checked);
@@ -703,19 +703,29 @@ void EditorFeatureProfileManager::_property_item_edited() {
String property_selected = md;
edited->set_disable_class_property(class_name, property_selected, !checked);
_save_and_update();
- _update_selected_profile();
+ _update_profile_tree_from(class_list->get_selected());
} else if (md.get_type() == Variant::INT) {
int feature_selected = md;
switch (feature_selected) {
case CLASS_OPTION_DISABLE_EDITOR: {
edited->set_disable_class_editor(class_name, !checked);
_save_and_update();
- _update_selected_profile();
+ _update_profile_tree_from(class_list->get_selected());
} break;
}
}
}
+void EditorFeatureProfileManager::_update_profile_tree_from(TreeItem *p_edited) {
+ String edited_class = p_edited->get_metadata(0);
+
+ TreeItem *edited_parent = p_edited->get_parent();
+ int class_insert_index = p_edited->get_index();
+ p_edited->get_parent()->remove_child(p_edited);
+
+ _fill_classes_from(edited_parent, edited_class, edited_class, class_insert_index);
+}
+
void EditorFeatureProfileManager::_update_selected_profile() {
String class_selected;
int feature_selected = -1;
diff --git a/editor/editor_feature_profile.h b/editor/editor_feature_profile.h
index 2fa6ae9813..7458a04e19 100644
--- a/editor/editor_feature_profile.h
+++ b/editor/editor_feature_profile.h
@@ -147,7 +147,8 @@ class EditorFeatureProfileManager : public AcceptDialog {
String current_profile;
void _update_profile_list(const String &p_select_profile = String());
void _update_selected_profile();
- void _fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected);
+ void _update_profile_tree_from(TreeItem *p_edited);
+ void _fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected, int p_class_insert_index = -1);
Ref<EditorFeatureProfile> current;
Ref<EditorFeatureProfile> edited;
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index a8d978c66d..48c0c7ac06 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -1967,7 +1967,7 @@ void EditorHelp::_update_doc() {
class_desc->add_text(argument.name);
class_desc->add_text(": ");
- _add_type(argument.type);
+ _add_type(argument.type, argument.enumeration, argument.is_bitfield);
if (!argument.default_value.is_empty()) {
class_desc->push_color(theme_cache.symbol_color);
@@ -2890,7 +2890,7 @@ void EditorHelp::_load_doc_thread(void *p_udata) {
callable_mp_static(&EditorHelp::_gen_extensions_docs).call_deferred();
} else {
// We have to go back to the main thread to start from scratch, bypassing any possibly existing cache.
- callable_mp_static(&EditorHelp::generate_doc).bind(false).call_deferred();
+ callable_mp_static(&EditorHelp::generate_doc).call_deferred(false);
}
OS::get_singleton()->benchmark_end_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count));
diff --git a/editor/editor_inspector.compat.inc b/editor/editor_inspector.compat.inc
new file mode 100644
index 0000000000..53c410ba26
--- /dev/null
+++ b/editor/editor_inspector.compat.inc
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* editor_inspector.compat.inc */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef DISABLE_DEPRECATED
+
+void EditorInspectorPlugin::_add_property_editor_bind_compat_92322(const String &p_for_property, Control *p_prop, bool p_add_to_end) {
+ add_property_editor(p_for_property, p_prop, p_add_to_end, "");
+}
+
+void EditorInspectorPlugin::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("add_property_editor", "property", "editor", "add_to_end"), &EditorInspectorPlugin::_add_property_editor_bind_compat_92322, DEFVAL(false));
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 8e608fb797..f4dcc8bd4a 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "editor_inspector.h"
+#include "editor_inspector.compat.inc"
#include "core/os/keyboard.h"
#include "editor/doc_tools.h"
@@ -1128,11 +1129,12 @@ void EditorInspectorPlugin::add_custom_control(Control *control) {
added_editors.push_back(ae);
}
-void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end) {
+void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end, const String &p_label) {
AddedEditor ae;
ae.properties.push_back(p_for_property);
ae.property_editor = p_prop;
ae.add_to_end = p_add_to_end;
+ ae.label = p_label;
added_editors.push_back(ae);
}
@@ -1174,7 +1176,7 @@ void EditorInspectorPlugin::parse_end(Object *p_object) {
void EditorInspectorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_custom_control", "control"), &EditorInspectorPlugin::add_custom_control);
- ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor", "add_to_end"), &EditorInspectorPlugin::add_property_editor, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor", "add_to_end", "label"), &EditorInspectorPlugin::add_property_editor, DEFVAL(false), DEFVAL(String()));
ClassDB::bind_method(D_METHOD("add_property_editor_for_multiple_properties", "label", "properties", "editor"), &EditorInspectorPlugin::add_property_editor_for_multiple_properties);
GDVIRTUAL_BIND(_can_handle, "object")
@@ -2727,6 +2729,8 @@ void EditorInspector::update_tree() {
// TODO: Can be useful to store more context for the focusable, such as the caret position in LineEdit.
StringName current_selected = property_selected;
int current_focusable = -1;
+ // Temporarily disable focus following to avoid jumping while the inspector is updating.
+ set_follow_focus(false);
if (property_focusable != -1) {
// Check that focusable is actually focusable.
@@ -3482,6 +3486,7 @@ void EditorInspector::update_tree() {
// Updating inspector might invalidate some editing owners.
EditorNode::get_singleton()->hide_unused_editors();
}
+ set_follow_focus(true);
}
void EditorInspector::update_property(const String &p_prop) {
@@ -3779,7 +3784,6 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
}
emit_signal(_prop_edited, p_name);
-
} else if (Object::cast_to<MultiNodeEdit>(object)) {
Object::cast_to<MultiNodeEdit>(object)->set_property_field(p_name, p_value, p_changed_field);
_edit_request_change(object, p_name);
@@ -3956,7 +3960,7 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
//property checked
if (autoclear) {
if (!p_checked) {
- object->set(p_path, Variant());
+ _edit_set(p_path, Variant(), false, "");
} else {
Variant to_create;
List<PropertyInfo> pinfo;
@@ -3968,7 +3972,7 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
break;
}
}
- object->set(p_path, to_create);
+ _edit_set(p_path, to_create, false, "");
}
if (editor_property_map.has(p_path)) {
@@ -3979,7 +3983,6 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
E->update_cache();
}
}
-
} else {
emit_signal(SNAME("property_toggled"), p_path, p_checked);
}
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 3cbee5c502..a0ced55bd8 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -252,9 +252,13 @@ protected:
GDVIRTUAL7R(bool, _parse_property, Object *, Variant::Type, String, PropertyHint, String, BitField<PropertyUsageFlags>, bool)
GDVIRTUAL1(_parse_end, Object *)
+#ifndef DISABLE_DEPRECATED
+ void _add_property_editor_bind_compat_92322(const String &p_for_property, Control *p_prop, bool p_add_to_end);
+ static void _bind_compatibility_methods();
+#endif // DISABLE_DEPRECATED
public:
void add_custom_control(Control *control);
- void add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end = false);
+ void add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end = false, const String &p_label = String());
void add_property_editor_for_multiple_properties(const String &p_label, const Vector<String> &p_properties, Control *p_prop);
virtual bool can_handle(Object *p_object);
@@ -344,7 +348,7 @@ class EditorInspectorArray : public EditorInspectorSection {
MODE_NONE,
MODE_USE_COUNT_PROPERTY,
MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION,
- } mode;
+ } mode = MODE_NONE;
StringName count_property;
StringName array_element_prefix;
String swap_method;
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 6615133dea..166d09af30 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -59,7 +59,7 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f
MessageType message_type = p_type == ERR_HANDLER_WARNING ? MSG_TYPE_WARNING : MSG_TYPE_ERROR;
if (self->current != Thread::get_caller_id()) {
- callable_mp(self, &EditorLog::add_message).bind(err_str, message_type).call_deferred();
+ callable_mp(self, &EditorLog::add_message).call_deferred(err_str, message_type);
} else {
self->add_message(err_str, message_type);
}
@@ -273,6 +273,10 @@ void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) {
}
void EditorLog::_rebuild_log() {
+ if (messages.is_empty()) {
+ return;
+ }
+
log->clear();
int line_count = 0;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 68fcc613ee..a436f63c3e 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -671,7 +671,7 @@ void EditorNode::_notification(int p_what) {
callable_mp(this, &EditorNode::_begin_first_scan).call_deferred();
- DisplayServer::get_singleton()->set_system_theme_change_callback(callable_mp(this, &EditorNode::_update_theme));
+ DisplayServer::get_singleton()->set_system_theme_change_callback(callable_mp(this, &EditorNode::_update_theme).bind(false));
/* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */
} break;
@@ -5541,7 +5541,7 @@ void EditorNode::_add_dropped_files_recursive(const Vector<String> &p_files, Str
}
void EditorNode::_file_access_close_error_notify(const String &p_str) {
- callable_mp_static(&EditorNode::_file_access_close_error_notify_impl).bind(p_str).call_deferred();
+ callable_mp_static(&EditorNode::_file_access_close_error_notify_impl).call_deferred(p_str);
}
void EditorNode::_file_access_close_error_notify_impl(const String &p_str) {
@@ -6163,7 +6163,7 @@ static Node *_resource_get_edited_scene() {
}
void EditorNode::_print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich) {
- callable_mp_static(&EditorNode::_print_handler_impl).bind(p_string, p_error, p_rich).call_deferred();
+ callable_mp_static(&EditorNode::_print_handler_impl).call_deferred(p_string, p_error, p_rich);
}
void EditorNode::_print_handler_impl(const String &p_string, bool p_error, bool p_rich) {
@@ -6297,6 +6297,14 @@ EditorNode::EditorNode() {
EditorSettings::create();
}
+ ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::L);
+ ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::L);
+ ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::G);
+ ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::G);
+
+ // Used in the GPUParticles/CPUParticles 2D/3D editor plugins.
+ ED_SHORTCUT("particles/restart_emission", TTR("Restart Emission"), KeyModifierMask::CTRL | Key::R);
+
FileAccess::set_backup_save(EDITOR_GET("filesystem/on_save/safe_save_on_backup_then_rename"));
_update_vsync_mode();
@@ -7220,6 +7228,8 @@ EditorNode::EditorNode() {
disk_changed = memnew(ConfirmationDialog);
{
+ disk_changed->set_title(TTR("Files have been modified on disk"));
+
VBoxContainer *vbc = memnew(VBoxContainer);
disk_changed->add_child(vbc);
@@ -7233,9 +7243,9 @@ EditorNode::EditorNode() {
disk_changed->connect("confirmed", callable_mp(this, &EditorNode::_reload_modified_scenes));
disk_changed->connect("confirmed", callable_mp(this, &EditorNode::_reload_project_settings));
- disk_changed->set_ok_button_text(TTR("Reload"));
+ disk_changed->set_ok_button_text(TTR("Discard local changes and reload"));
- disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
+ disk_changed->add_button(TTR("Keep local changes and overwrite"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
disk_changed->connect("custom_action", callable_mp(this, &EditorNode::_resave_scenes));
}
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 49b30bd06e..103ea3ffc3 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -2645,7 +2645,7 @@ void EditorPropertyColor::_color_changed(const Color &p_color) {
}
void EditorPropertyColor::_popup_closed() {
- get_edited_object()->set(get_edited_property(), last_color);
+ get_edited_object()->set(get_edited_property(), was_checked ? Variant(last_color) : Variant());
if (!picker->get_pick_color().is_equal_approx(last_color)) {
emit_changed(get_edited_property(), picker->get_pick_color(), "", false);
}
@@ -2653,6 +2653,7 @@ void EditorPropertyColor::_popup_closed() {
void EditorPropertyColor::_picker_opening() {
last_color = picker->get_pick_color();
+ was_checked = !is_checkable() || is_checked();
}
void EditorPropertyColor::_notification(int p_what) {
@@ -3217,7 +3218,7 @@ void EditorPropertyResource::_open_editor_pressed() {
Ref<Resource> res = get_edited_property_value();
if (res.is_valid()) {
// May clear the editor so do it deferred.
- callable_mp(EditorNode::get_singleton(), &EditorNode::edit_item).bind(res.ptr(), this).call_deferred();
+ callable_mp(EditorNode::get_singleton(), &EditorNode::edit_item).call_deferred(res.ptr(), this);
}
}
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index d16c80bfc4..f2c5497e4f 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -625,6 +625,7 @@ class EditorPropertyColor : public EditorProperty {
Color last_color;
bool live_changes_enabled = true;
+ bool was_checked = false;
protected:
virtual void _set_read_only(bool p_read_only) override;
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index ca1070d7f3..b4fc47323a 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -960,6 +960,7 @@ void EditorPropertyDictionary::update_property() {
memdelete(container);
button_add_item = nullptr;
container = nullptr;
+ add_panel = nullptr;
slots.clear();
}
return;
diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp
index 2c6c13401f..b1f3875175 100644
--- a/editor/editor_settings_dialog.cpp
+++ b/editor/editor_settings_dialog.cpp
@@ -30,14 +30,13 @@
#include "editor_settings_dialog.h"
-#include "core/config/project_settings.h"
#include "core/input/input_map.h"
#include "core/os/keyboard.h"
#include "editor/debugger/editor_debugger_node.h"
-#include "editor/editor_file_system.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_property_name_processor.h"
+#include "editor/editor_sectioned_inspector.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
@@ -45,7 +44,9 @@
#include "editor/input_event_configuration_dialog.h"
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
-#include "scene/gui/margin_container.h"
+#include "scene/gui/panel_container.h"
+#include "scene/gui/tab_container.h"
+#include "scene/gui/texture_rect.h"
void EditorSettingsDialog::ok_pressed() {
if (!EditorSettings::get_singleton()) {
@@ -499,6 +500,9 @@ void EditorSettingsDialog::_update_shortcuts() {
memdelete(section);
}
}
+
+ // Update UI.
+ clear_all_search->set_disabled(shortcut_search_box->get_text().is_empty() && shortcut_search_by_event->get_event().is_null());
}
void EditorSettingsDialog::_shortcut_button_pressed(Object *p_item, int p_column, int p_idx, MouseButton p_button) {
@@ -782,8 +786,9 @@ EditorSettingsDialog::EditorSettingsDialog() {
shortcut_search_by_event->connect(SceneStringName(focus_exited), callable_mp((AcceptDialog *)this, &AcceptDialog::set_close_on_escape).bind(true));
top_hbox->add_child(shortcut_search_by_event);
- Button *clear_all_search = memnew(Button);
+ clear_all_search = memnew(Button);
clear_all_search->set_text(TTR("Clear All"));
+ clear_all_search->set_tooltip_text(TTR("Clear all search filters."));
clear_all_search->connect(SceneStringName(pressed), callable_mp(shortcut_search_box, &LineEdit::clear));
clear_all_search->connect(SceneStringName(pressed), callable_mp(shortcut_search_by_event, &EventListenerLineEdit::clear_event));
top_hbox->add_child(clear_all_search);
diff --git a/editor/editor_settings_dialog.h b/editor/editor_settings_dialog.h
index fdfc0a43ec..cab8fe9da1 100644
--- a/editor/editor_settings_dialog.h
+++ b/editor/editor_settings_dialog.h
@@ -32,13 +32,14 @@
#define EDITOR_SETTINGS_DIALOG_H
#include "editor/action_map_editor.h"
-#include "editor/editor_inspector.h"
-#include "editor/editor_sectioned_inspector.h"
#include "scene/gui/dialogs.h"
-#include "scene/gui/panel_container.h"
-#include "scene/gui/rich_text_label.h"
-#include "scene/gui/tab_container.h"
-#include "scene/gui/texture_rect.h"
+
+class PanelContainer;
+class SectionedInspector;
+class TabContainer;
+class TextureRect;
+class Tree;
+class TreeItem;
class EditorSettingsDialog : public AcceptDialog {
GDCLASS(EditorSettingsDialog, AcceptDialog);
@@ -53,6 +54,7 @@ class EditorSettingsDialog : public AcceptDialog {
LineEdit *shortcut_search_box = nullptr;
EventListenerLineEdit *shortcut_search_by_event = nullptr;
SectionedInspector *inspector = nullptr;
+ Button *clear_all_search = nullptr;
// Shortcuts
enum ShortcutButton {
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index c6a671b7f9..dd283ebfd6 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -131,7 +131,7 @@ void ProjectExportDialog::popup_export() {
if (saved_size != Rect2()) {
popup(saved_size);
} else {
- popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8);
+ popup_centered_clamped(Size2(900, 500) * EDSCALE, 0.7);
}
}
@@ -1311,9 +1311,15 @@ ProjectExportDialog::ProjectExportDialog() {
// Resources export parameters.
+ ScrollContainer *resources_scroll_container = memnew(ScrollContainer);
+ resources_scroll_container->set_name(TTR("Resources"));
+ resources_scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
+ sections->add_child(resources_scroll_container);
+
VBoxContainer *resources_vb = memnew(VBoxContainer);
- sections->add_child(resources_vb);
- resources_vb->set_name(TTR("Resources"));
+ resources_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ resources_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ resources_scroll_container->add_child(resources_vb);
export_filter = memnew(OptionButton);
export_filter->add_item(TTR("Export all resources in the project"));
@@ -1332,6 +1338,7 @@ ProjectExportDialog::ProjectExportDialog() {
resources_vb->add_child(include_margin);
include_files = memnew(Tree);
+ include_files->set_custom_minimum_size(Size2(1, 75 * EDSCALE));
include_margin->add_child(include_files);
include_files->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
include_files->connect("item_edited", callable_mp(this, &ProjectExportDialog::_tree_changed));
@@ -1384,18 +1391,25 @@ ProjectExportDialog::ProjectExportDialog() {
VBoxContainer *feature_vb = memnew(VBoxContainer);
feature_vb->set_name(TTR("Features"));
+ feature_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
custom_features = memnew(LineEdit);
custom_features->connect("text_changed", callable_mp(this, &ProjectExportDialog::_custom_features_changed));
feature_vb->add_margin_child(TTR("Custom (comma-separated):"), custom_features);
custom_feature_display = memnew(RichTextLabel);
+ custom_feature_display->set_custom_minimum_size(Size2(1, 75 * EDSCALE));
custom_feature_display->set_v_size_flags(Control::SIZE_EXPAND_FILL);
feature_vb->add_margin_child(TTR("Feature List:"), custom_feature_display, true);
sections->add_child(feature_vb);
// Encryption export parameters.
+ ScrollContainer *sec_scroll_container = memnew(ScrollContainer);
+ sec_scroll_container->set_name(TTR("Encryption"));
+ sec_scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
+
VBoxContainer *sec_vb = memnew(VBoxContainer);
- sec_vb->set_name(TTR("Encryption"));
+ sec_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ sec_scroll_container->add_child(sec_vb);
enc_pck = memnew(CheckButton);
enc_pck->connect("toggled", callable_mp(this, &ProjectExportDialog::_enc_pck_changed));
@@ -1426,7 +1440,7 @@ ProjectExportDialog::ProjectExportDialog() {
script_key_error->add_theme_color_override("font_color", EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("error_color"), EditorStringName(Editor)));
sec_vb->add_margin_child(TTR("Encryption Key (256-bits as hexadecimal):"), script_key);
sec_vb->add_child(script_key_error);
- sections->add_child(sec_vb);
+ sections->add_child(sec_scroll_container);
Label *sec_info = memnew(Label);
sec_info->set_text(TTR("Note: Encryption key needs to be stored in the binary,\nyou need to build the export templates from source."));
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index b05cafa694..2e88540fc4 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -79,6 +79,15 @@ Control *FileSystemList::make_custom_tooltip(const String &p_text) const {
}
void FileSystemList::_line_editor_submit(const String &p_text) {
+ if (popup_edit_commited) {
+ return; // Already processed by _text_editor_popup_modal_close
+ }
+
+ if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) {
+ return; // ESC pressed, app focus lost, or forced close from code.
+ }
+
+ popup_edit_commited = true; // End edit popup processing.
popup_editor->hide();
emit_signal(SNAME("item_edited"));
@@ -127,6 +136,7 @@ bool FileSystemList::edit_selected() {
line_editor->set_text(name);
line_editor->select(0, name.rfind("."));
+ popup_edit_commited = false; // Start edit popup processing.
popup_editor->popup();
popup_editor->child_controls_changed();
line_editor->grab_focus();
@@ -138,8 +148,12 @@ String FileSystemList::get_edit_text() {
}
void FileSystemList::_text_editor_popup_modal_close() {
+ if (popup_edit_commited) {
+ return; // Already processed by _text_editor_popup_modal_close
+ }
+
if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) {
- return;
+ return; // ESC pressed, app focus lost, or forced close from code.
}
_line_editor_submit(line_editor->get_text());
@@ -3265,7 +3279,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect
if (p_paths.size() == 1) {
const String &fpath = p_paths[0];
- bool added_separator = false;
+ [[maybe_unused]] bool added_separator = false;
if (favorites_list.has(fpath)) {
TreeItem *favorites_item = tree->get_root()->get_first_child();
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 7449657c06..3fbff3ef19 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -59,6 +59,7 @@ class FileSystemTree : public Tree {
class FileSystemList : public ItemList {
GDCLASS(FileSystemList, ItemList);
+ bool popup_edit_commited = true;
VBoxContainer *popup_editor_vb = nullptr;
Popup *popup_editor = nullptr;
LineEdit *line_editor = nullptr;
diff --git a/editor/gui/editor_toaster.cpp b/editor/gui/editor_toaster.cpp
index 7aa5335b77..df6c494392 100644
--- a/editor/gui/editor_toaster.cpp
+++ b/editor/gui/editor_toaster.cpp
@@ -149,7 +149,7 @@ void EditorToaster::_notification(int p_what) {
void EditorToaster::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
// This may be called from a thread. Since we will deal with non-thread-safe elements,
// we have to put it in the queue for safety.
- callable_mp_static(&EditorToaster::_error_handler_impl).bind(String::utf8(p_file), p_line, String::utf8(p_error), String::utf8(p_errorexp), p_editor_notify, p_type).call_deferred();
+ callable_mp_static(&EditorToaster::_error_handler_impl).call_deferred(String::utf8(p_file), p_line, String::utf8(p_error), String::utf8(p_errorexp), p_editor_notify, p_type);
}
void EditorToaster::_error_handler_impl(const String &p_file, int p_line, const String &p_error, const String &p_errorexp, bool p_editor_notify, int p_type) {
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index a06dd310ea..ddf22c46e6 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -614,9 +614,9 @@ void SceneTreeEditor::_update_tree(bool p_scroll_to_selected) {
updating_tree = true;
tree->clear();
+ last_hash = hash_djb2_one_64(0);
if (get_scene_node()) {
_add_nodes(get_scene_node(), nullptr);
- last_hash = hash_djb2_one_64(0);
_compute_hash(get_scene_node(), last_hash);
}
updating_tree = false;
@@ -1368,22 +1368,35 @@ bool SceneTreeEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_d
}
bool scene_drop = true;
+ bool audio_drop = true;
for (int i = 0; i < files.size(); i++) {
String ftype = EditorFileSystem::get_singleton()->get_file_type(files[i]);
if (ftype != "PackedScene") {
scene_drop = false;
- break;
+ }
+ if (audio_drop && !ClassDB::is_parent_class(ftype, "AudioStream")) {
+ audio_drop = false;
}
}
if (scene_drop) {
tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM);
- } else {
+ return true;
+ }
+
+ if (audio_drop) {
if (files.size() > 1) {
- return false;
+ tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN);
+ } else {
+ tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM);
}
- tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM);
+ return true;
+ }
+
+ if (files.size() > 1) {
+ return false;
}
+ tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM);
return true;
}
diff --git a/editor/icons/AudioStreamPlayer.svg b/editor/icons/AudioStreamPlayer.svg
index 317b25d3e9..ba84502d99 100644
--- a/editor/icons/AudioStreamPlayer.svg
+++ b/editor/icons/AudioStreamPlayer.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" gradientUnits="userSpaceOnUse" x2="0" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="M9 14a1 1 0 0 0 1.5.85l4-2.511a1 1 0 0 0 0-1.724l-4-2.511a1 1 0 0 0-1.5.85z" fill="#e0e0e0"/><path d="M13 2a1 1 0 0 0-1-1L4.754 3A1 1 0 0 0 4 4v5.55A2.5 2.5 0 1 0 6 12V4.756l5-1.428V6.5l2-1z" fill="url(#a)"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path d="M1.382.335.777.858H.204v.673h.564l.614.531Z" style="fill:#e0e0e0;stroke:#e0e0e0;stroke-width:.1764;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"/><path d="M1.718.572c.275.374.275.882 0 1.256M1.947.343c.402.5.402 1.213 0 1.714" style="fill:none;stroke:#e0e0e0;stroke-width:.176328;stroke-linecap:round;stroke-opacity:1"/></svg> \ No newline at end of file
diff --git a/editor/icons/AudioStreamPlayer2D.svg b/editor/icons/AudioStreamPlayer2D.svg
index 87beccd19a..54ff128a4d 100644
--- a/editor/icons/AudioStreamPlayer2D.svg
+++ b/editor/icons/AudioStreamPlayer2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" gradientUnits="userSpaceOnUse" x2="0" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="M9 14a1 1 0 0 0 1.5.85l4-2.511a1 1 0 0 0 0-1.724l-4-2.511a1 1 0 0 0-1.5.85z" fill="#8da5f3"/><path d="M13 2a1 1 0 0 0-1-1L4.754 3A1 1 0 0 0 4 4v5.55A2.5 2.5 0 1 0 6 12V4.756l5-1.428V6.5l2-1z" fill="url(#a)"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path d="M1.382.335.777.858H.204v.673h.564l.614.531Z" style="fill:#8da5f3;stroke:#8da5f3;stroke-width:.1764;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"/><path d="M1.718.572c.275.374.275.882 0 1.256M1.947.343c.402.5.402 1.213 0 1.714" style="fill:none;stroke:#8da5f3;stroke-width:.176328;stroke-linecap:round;stroke-opacity:1"/></svg> \ No newline at end of file
diff --git a/editor/icons/AudioStreamPlayer3D.svg b/editor/icons/AudioStreamPlayer3D.svg
index 440a5e52ef..fc0231a657 100644
--- a/editor/icons/AudioStreamPlayer3D.svg
+++ b/editor/icons/AudioStreamPlayer3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" gradientUnits="userSpaceOnUse" x2="0" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="M9 14a1 1 0 0 0 1.5.85l4-2.511a1 1 0 0 0 0-1.724l-4-2.511a1 1 0 0 0-1.5.85z" fill="#fc7f7f"/><path d="M13 2a1 1 0 0 0-1-1L4.754 3A1 1 0 0 0 4 4v5.55A2.5 2.5 0 1 0 6 12V4.756l5-1.428V6.5l2-1z" fill="url(#a)"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path d="M1.382.335.777.858H.204v.673h.564l.614.531Z" style="fill:#fc7f7f;stroke:#fc7f7f;stroke-width:.1764;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"/><path d="M1.718.572c.275.374.275.882 0 1.256M1.947.343c.402.5.402 1.213 0 1.714" style="fill:none;stroke:#fc7f7f;stroke-width:.176328;stroke-linecap:round;stroke-opacity:1"/></svg> \ No newline at end of file
diff --git a/editor/icons/SCsub b/editor/icons/SCsub
index d2d752cff4..0d9ac43c46 100644
--- a/editor/icons/SCsub
+++ b/editor/icons/SCsub
@@ -3,8 +3,8 @@
Import("env")
import os
-import editor_icons_builders
+import editor_icons_builders
env["BUILDERS"]["MakeEditorIconsBuilder"] = Builder(
action=env.Run(editor_icons_builders.make_editor_icons_action),
diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index 070c44419a..c0d38af26a 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -279,7 +279,7 @@ bool ResourceImporterScene::get_option_visibility(const String &p_path, const St
}
}
- if (animation_importer && (p_option.begins_with("nodes/") || p_option.begins_with("meshes/") || p_option.begins_with("skins/"))) {
+ if (animation_importer && (p_option == "nodes/root_type" || p_option == "nodes/root_name" || p_option.begins_with("meshes/") || p_option.begins_with("skins/"))) {
return false; // Nothing to do here for animations.
}
@@ -2329,6 +2329,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
}
script_ext_hint += "*." + E;
}
+ bool trimming_defaults_on = p_path.get_extension().to_lower() == "fbx";
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/apply_root_scale"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0));
@@ -2342,7 +2343,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), trimming_defaults_on));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/remove_immutable_tracks"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import_rest_as_RESET"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
diff --git a/editor/import/resource_importer_shader_file.cpp b/editor/import/resource_importer_shader_file.cpp
index bde2e3d0bf..6b20a8c9d5 100644
--- a/editor/import/resource_importer_shader_file.cpp
+++ b/editor/import/resource_importer_shader_file.cpp
@@ -106,7 +106,7 @@ Error ResourceImporterShaderFile::import(const String &p_source_file, const Stri
if (err != OK) {
if (!ShaderFileEditor::singleton->is_visible_in_tree()) {
- callable_mp_static(&EditorNode::add_io_error).bind(vformat(TTR("Error importing GLSL shader file: '%s'. Open the file in the filesystem dock in order to see the reason."), p_source_file)).call_deferred();
+ callable_mp_static(&EditorNode::add_io_error).call_deferred(vformat(TTR("Error importing GLSL shader file: '%s'. Open the file in the filesystem dock in order to see the reason."), p_source_file));
}
}
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index 8cf104725a..487b8fc175 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -269,9 +269,9 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I
for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
Vector<uint8_t> data;
if (use_webp) {
- data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i));
+ data = Image::webp_lossless_packer(i ? p_image->get_image_from_mipmap(i) : p_image);
} else {
- data = Image::png_packer(p_image->get_image_from_mipmap(i));
+ data = Image::png_packer(i ? p_image->get_image_from_mipmap(i) : p_image);
}
int data_len = data.size();
f->store_32(data_len);
@@ -289,7 +289,7 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I
f->store_32(p_image->get_format());
for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
- Vector<uint8_t> data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality);
+ Vector<uint8_t> data = Image::webp_lossy_packer(i ? p_image->get_image_from_mipmap(i) : p_image, p_lossy_quality);
int data_len = data.size();
f->store_32(data_len);
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index fbe76e1d5c..2d87e6592f 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -188,10 +188,20 @@ void ImportDock::_update_options(const String &p_path, const Ref<ConfigFile> &p_
params->checked.clear();
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);
+ }
+
for (const ResourceImporter::ImportOption &E : options) {
params->properties.push_back(E.option);
- if (p_config.is_valid() && p_config->has_section_key("params", E.option.name)) {
- params->values[E.option.name] = p_config->get_value("params", E.option.name);
+ if (p_config.is_valid() && import_options.has(E.option.name)) {
+ params->values[E.option.name] = import_options[E.option.name];
} else {
params->values[E.option.name] = E.default_value;
}
diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp
index 865729c7c3..2ecce2f739 100644
--- a/editor/input_event_configuration_dialog.cpp
+++ b/editor/input_event_configuration_dialog.cpp
@@ -446,6 +446,11 @@ void InputEventConfigurationDialog::_key_location_selected(int p_location) {
_set_event(k, original_event);
}
+void InputEventConfigurationDialog::_input_list_item_activated() {
+ TreeItem *selected = input_list_tree->get_selected();
+ selected->set_collapsed(!selected->is_collapsed());
+}
+
void InputEventConfigurationDialog::_input_list_item_selected() {
TreeItem *selected = input_list_tree->get_selected();
@@ -670,6 +675,7 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
input_list_tree = memnew(Tree);
input_list_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
input_list_tree->set_custom_minimum_size(Size2(0, 100 * EDSCALE)); // Min height for tree
+ input_list_tree->connect("item_activated", callable_mp(this, &InputEventConfigurationDialog::_input_list_item_activated));
input_list_tree->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_input_list_item_selected));
input_list_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
manual_vbox->add_child(input_list_tree);
diff --git a/editor/input_event_configuration_dialog.h b/editor/input_event_configuration_dialog.h
index 1d2cc8ba36..b27f25a5b7 100644
--- a/editor/input_event_configuration_dialog.h
+++ b/editor/input_event_configuration_dialog.h
@@ -107,6 +107,7 @@ private:
void _search_term_updated(const String &p_term);
void _update_input_list();
+ void _input_list_item_activated();
void _input_list_item_selected();
void _mod_toggled(bool p_checked, int p_index);
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 1366a38bec..62369cc2c1 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -1711,7 +1711,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2_step_prepare(int p_step_offs
OS::get_singleton()->get_main_loop()->process(0);
// This is the key: process the frame and let all callbacks/updates/notifications happen
// so everything (transforms, skeletons, etc.) is up-to-date visually.
- callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_2_step_capture).bind(p_step_offset, p_capture_idx).call_deferred();
+ callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_2_step_capture).call_deferred(p_step_offset, p_capture_idx);
return;
} else {
next_capture_idx++;
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index a44430ca7f..8b44d6b486 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -46,10 +46,12 @@
#include "editor/scene_tree_dock.h"
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
+#include "scene/2d/audio_stream_player_2d.h"
#include "scene/2d/polygon_2d.h"
#include "scene/2d/skeleton_2d.h"
#include "scene/2d/sprite_2d.h"
#include "scene/2d/touch_screen_button.h"
+#include "scene/gui/base_button.h"
#include "scene/gui/flow_container.h"
#include "scene/gui/grid_container.h"
#include "scene/gui/separator.h"
@@ -966,7 +968,7 @@ void CanvasItemEditor::_add_node_pressed(int p_result) {
}
}
-void CanvasItemEditor::_node_created(Node *p_node) {
+void CanvasItemEditor::_adjust_new_node_position(Node *p_node) {
if (node_create_position == Point2()) {
return;
}
@@ -5161,7 +5163,7 @@ CanvasItemEditor::CanvasItemEditor() {
editor_selection->connect("selection_changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
editor_selection->connect("selection_changed", callable_mp(this, &CanvasItemEditor::_selection_changed));
- SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created));
+ SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_adjust_new_node_position));
SceneTreeDock::get_singleton()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position));
// Add some margin to the sides for better esthetics.
@@ -5417,7 +5419,7 @@ CanvasItemEditor::CanvasItemEditor() {
lock_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_popup_callback).bind(LOCK_SELECTED));
lock_button->set_tooltip_text(TTR("Lock selected node, preventing selection and movement."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- lock_button->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::L));
+ lock_button->set_shortcut(ED_GET_SHORTCUT("editor/lock_selected_nodes"));
unlock_button = memnew(Button);
unlock_button->set_theme_type_variation("FlatButton");
@@ -5425,7 +5427,7 @@ CanvasItemEditor::CanvasItemEditor() {
unlock_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_popup_callback).bind(UNLOCK_SELECTED));
unlock_button->set_tooltip_text(TTR("Unlock selected node, allowing selection and movement."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- unlock_button->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::L));
+ unlock_button->set_shortcut(ED_GET_SHORTCUT("editor/unlock_selected_nodes"));
group_button = memnew(Button);
group_button->set_theme_type_variation("FlatButton");
@@ -5433,7 +5435,7 @@ CanvasItemEditor::CanvasItemEditor() {
group_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_popup_callback).bind(GROUP_SELECTED));
group_button->set_tooltip_text(TTR("Groups the selected node with its children. This causes the parent to be selected when any child node is clicked in 2D and 3D view."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- group_button->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::G));
+ group_button->set_shortcut(ED_GET_SHORTCUT("editor/group_selected_nodes"));
ungroup_button = memnew(Button);
ungroup_button->set_theme_type_variation("FlatButton");
@@ -5441,7 +5443,7 @@ CanvasItemEditor::CanvasItemEditor() {
ungroup_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_popup_callback).bind(UNGROUP_SELECTED));
ungroup_button->set_tooltip_text(TTR("Ungroups the selected node from its children. Child nodes will be individual items in 2D and 3D view."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- ungroup_button->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::G));
+ ungroup_button->set_shortcut(ED_GET_SHORTCUT("editor/ungroup_selected_nodes"));
main_menu_hbox->add_child(memnew(VSeparator));
@@ -5636,7 +5638,7 @@ CanvasItemEditor::CanvasItemEditor() {
clear(); // Make sure values are initialized.
// Update the menus' checkboxes.
- callable_mp(this, &CanvasItemEditor::set_state).bind(get_state()).call_deferred();
+ callable_mp(this, &CanvasItemEditor::set_state).call_deferred(get_state());
}
CanvasItemEditor *CanvasItemEditor::singleton = nullptr;
@@ -5697,15 +5699,15 @@ CanvasItemEditorPlugin::~CanvasItemEditorPlugin() {
}
void CanvasItemEditorViewport::_on_mouse_exit() {
- if (!selector->is_visible()) {
+ if (!texture_node_type_selector->is_visible()) {
_remove_preview();
}
}
-void CanvasItemEditorViewport::_on_select_type(Object *selected) {
+void CanvasItemEditorViewport::_on_select_texture_node_type(Object *selected) {
CheckBox *check = Object::cast_to<CheckBox>(selected);
String type = check->get_text();
- selector->set_title(vformat(TTR("Add %s"), type));
+ texture_node_type_selector->set_title(vformat(TTR("Add %s"), type));
label->set_text(vformat(TTR("Adding %s..."), type));
}
@@ -5717,7 +5719,7 @@ void CanvasItemEditorViewport::_on_change_type_confirmed() {
CheckBox *check = Object::cast_to<CheckBox>(button_group->get_pressed_button());
default_texture_node_type = check->get_text();
_perform_drop_data();
- selector->hide();
+ texture_node_type_selector->hide();
}
void CanvasItemEditorViewport::_on_change_type_closed() {
@@ -5728,35 +5730,35 @@ void CanvasItemEditorViewport::_create_preview(const Vector<String> &files) cons
bool add_preview = false;
for (int i = 0; i < files.size(); i++) {
Ref<Resource> res = ResourceLoader::load(files[i]);
- ERR_FAIL_COND(res.is_null());
- Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(*res));
- Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
- if (texture != nullptr || scene != nullptr) {
- String desc = TTR("Drag and drop to add as sibling of selected node (except when root is selected).") +
- "\n" + TTR("Hold Shift when dropping to add as child of selected node.") +
- "\n" + TTR("Hold Alt when dropping to add as child of root node.");
-
- if (texture != nullptr) {
- Sprite2D *sprite = memnew(Sprite2D);
- sprite->set_texture(texture);
- sprite->set_modulate(Color(1, 1, 1, 0.7f));
- preview_node->add_child(sprite);
- label->show();
- label_desc->show();
- desc += "\n" + TTR("Hold Alt + Shift when dropping to add as a different node type.");
- label_desc->set_text(desc);
- } else {
- if (scene.is_valid()) {
- Node *instance = scene->instantiate();
- if (instance) {
- preview_node->add_child(instance);
- label_desc->show();
- label_desc->set_text(desc);
- }
- }
+ ERR_CONTINUE(res.is_null());
+
+ Ref<Texture2D> texture = res;
+ if (texture.is_valid()) {
+ Sprite2D *sprite = memnew(Sprite2D);
+ sprite->set_texture(texture);
+ sprite->set_modulate(Color(1, 1, 1, 0.7f));
+ preview_node->add_child(sprite);
+ add_preview = true;
+ }
+
+ Ref<PackedScene> scene = res;
+ if (scene.is_valid()) {
+ Node *instance = scene->instantiate();
+ if (instance) {
+ preview_node->add_child(instance);
}
add_preview = true;
}
+
+ Ref<AudioStream> audio = res;
+ if (audio.is_valid()) {
+ Sprite2D *sprite = memnew(Sprite2D);
+ sprite->set_texture(get_editor_theme_icon(SNAME("AudioStreamPlayer2D")));
+ sprite->set_modulate(Color(1, 1, 1, 0.7f));
+ sprite->set_position(Vector2(0, -sprite->get_texture()->get_size().height) * EDSCALE);
+ preview_node->add_child(sprite);
+ add_preview = true;
+ }
}
if (add_preview) {
@@ -5797,44 +5799,46 @@ bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String &p_targe
return false;
}
-void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &path, const Point2 &p_point) {
+void CanvasItemEditorViewport::_create_texture_node(Node *p_parent, Node *p_child, const String &p_path, const Point2 &p_point) {
// Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
- String name = path.get_file().get_basename();
- child->set_name(Node::adjust_name_casing(name));
+ const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
+ if (!node_name.is_empty()) {
+ p_child->set_name(node_name);
+ }
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- Ref<Texture2D> texture = ResourceCache::get_ref(path);
+ Ref<Texture2D> texture = ResourceCache::get_ref(p_path);
- if (parent) {
- undo_redo->add_do_method(parent, "add_child", child, true);
- undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_reference(child);
- undo_redo->add_undo_method(parent, "remove_child", child);
+ if (p_parent) {
+ undo_redo->add_do_method(p_parent, "add_child", p_child, true);
+ undo_redo->add_do_method(p_child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->add_do_reference(p_child);
+ undo_redo->add_undo_method(p_parent, "remove_child", p_child);
} else { // If no parent is selected, set as root node of the scene.
- undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child);
- undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_reference(child);
+ undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", p_child);
+ undo_redo->add_do_method(p_child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->add_do_reference(p_child);
undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
}
- if (parent) {
- String new_name = parent->validate_child_name(child);
+ if (p_parent) {
+ String new_name = p_parent->validate_child_name(p_child);
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
- undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), child->get_class(), new_name);
- undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
+ undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), p_child->get_class(), new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
}
- if (Object::cast_to<TouchScreenButton>(child) || Object::cast_to<TextureButton>(child)) {
- undo_redo->add_do_property(child, "texture_normal", texture);
+ if (Object::cast_to<TouchScreenButton>(p_child) || Object::cast_to<TextureButton>(p_child)) {
+ undo_redo->add_do_property(p_child, "texture_normal", texture);
} else {
- undo_redo->add_do_property(child, "texture", texture);
+ undo_redo->add_do_property(p_child, "texture", texture);
}
// make visible for certain node type
- if (Object::cast_to<Control>(child)) {
+ if (Object::cast_to<Control>(p_child)) {
Size2 texture_size = texture->get_size();
- undo_redo->add_do_property(child, "size", texture_size);
- } else if (Object::cast_to<Polygon2D>(child)) {
+ undo_redo->add_do_property(p_child, "size", texture_size);
+ } else if (Object::cast_to<Polygon2D>(p_child)) {
Size2 texture_size = texture->get_size();
Vector<Vector2> list = {
Vector2(0, 0),
@@ -5842,7 +5846,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
Vector2(texture_size.width, texture_size.height),
Vector2(0, texture_size.height)
};
- undo_redo->add_do_property(child, "polygon", list);
+ undo_redo->add_do_property(p_child, "polygon", list);
}
// Compute the global position
@@ -5850,21 +5854,68 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
Point2 target_position = xform.affine_inverse().xform(p_point);
// Adjust position for Control and TouchScreenButton
- if (Object::cast_to<Control>(child) || Object::cast_to<TouchScreenButton>(child)) {
+ if (Object::cast_to<Control>(p_child) || Object::cast_to<TouchScreenButton>(p_child)) {
target_position -= texture->get_size() / 2;
}
- // there's nothing to be used as source position so snapping will work as absolute if enabled
+ // There's nothing to be used as source position, so snapping will work as absolute if enabled.
+ target_position = canvas_item_editor->snap_point(target_position);
+
+ CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent);
+ Point2 local_target_pos = parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position;
+
+ undo_redo->add_do_method(p_child, "set_position", local_target_pos);
+}
+
+void CanvasItemEditorViewport::_create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point) {
+ AudioStreamPlayer2D *child = memnew(AudioStreamPlayer2D);
+ child->set_stream(ResourceCache::get_ref(p_path));
+
+ // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
+ const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
+ if (!node_name.is_empty()) {
+ child->set_name(node_name);
+ }
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+
+ if (p_parent) {
+ undo_redo->add_do_method(p_parent, "add_child", child, true);
+ undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->add_do_reference(child);
+ undo_redo->add_undo_method(p_parent, "remove_child", child);
+ } else { // If no parent is selected, set as root node of the scene.
+ undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child);
+ undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->add_do_reference(child);
+ undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
+ }
+
+ if (p_parent) {
+ String new_name = p_parent->validate_child_name(child);
+ EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
+ undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), child->get_class(), new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
+ }
+
+ // Compute the global position
+ Transform2D xform = canvas_item_editor->get_canvas_transform();
+ Point2 target_position = xform.affine_inverse().xform(p_point);
+
+ // There's nothing to be used as source position, so snapping will work as absolute if enabled.
target_position = canvas_item_editor->snap_point(target_position);
- CanvasItem *parent_ci = Object::cast_to<CanvasItem>(parent);
+ CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent);
Point2 local_target_pos = parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position;
undo_redo->add_do_method(child, "set_position", local_target_pos);
+
+ EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
+ undo_redo->add_do_method(editor_selection, "add_node", child);
}
-bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) {
- Ref<PackedScene> sdata = ResourceLoader::load(path);
+bool CanvasItemEditorViewport::_create_instance(Node *p_parent, const String &p_path, const Point2 &p_point) {
+ Ref<PackedScene> sdata = ResourceLoader::load(p_path);
if (!sdata.is_valid()) { // invalid scene
return false;
}
@@ -5883,27 +5934,27 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons
}
}
- instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
+ instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(p_path));
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
- undo_redo->add_do_method(parent, "add_child", instantiated_scene, true);
+ undo_redo->add_do_method(p_parent, "add_child", instantiated_scene, true);
undo_redo->add_do_method(instantiated_scene, "set_owner", edited_scene);
undo_redo->add_do_reference(instantiated_scene);
- undo_redo->add_undo_method(parent, "remove_child", instantiated_scene);
+ undo_redo->add_undo_method(p_parent, "remove_child", instantiated_scene);
undo_redo->add_do_method(editor_selection, "add_node", instantiated_scene);
- String new_name = parent->validate_child_name(instantiated_scene);
+ String new_name = p_parent->validate_child_name(instantiated_scene);
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
- undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(parent), path, new_name);
- undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + new_name));
+ undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(p_parent), p_path, new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)) + "/" + new_name));
CanvasItem *instance_ci = Object::cast_to<CanvasItem>(instantiated_scene);
if (instance_ci) {
Vector2 target_pos = canvas_item_editor->get_canvas_transform().affine_inverse().xform(p_point);
target_pos = canvas_item_editor->snap_point(target_pos);
- CanvasItem *parent_ci = Object::cast_to<CanvasItem>(parent);
+ CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent);
if (parent_ci) {
target_pos = parent_ci->get_global_transform_with_canvas().affine_inverse().xform(target_pos);
}
@@ -5922,12 +5973,8 @@ void CanvasItemEditorViewport::_perform_drop_data() {
_remove_preview();
if (!target_node) {
- // Without root dropping multiple files is not allowed
- if (selected_files.size() > 1) {
- accept->set_text(TTR("Cannot instantiate multiple nodes without root."));
- accept->popup_centered();
- return;
- }
+ // Should already be handled by `can_drop_data`.
+ ERR_FAIL_COND_MSG(selected_files.size() > 1, "Can't instantiate multiple nodes without root.");
const String &path = selected_files[0];
Ref<Resource> res = ResourceLoader::load(path);
@@ -5973,9 +6020,14 @@ void CanvasItemEditorViewport::_perform_drop_data() {
Ref<Texture2D> texture = res;
if (texture.is_valid()) {
Node *child = Object::cast_to<Node>(ClassDB::instantiate(default_texture_node_type));
- _create_nodes(target_node, child, path, drop_pos);
+ _create_texture_node(target_node, child, path, drop_pos);
undo_redo->add_do_method(editor_selection, "add_node", child);
}
+
+ Ref<AudioStream> audio = res;
+ if (audio.is_valid()) {
+ _create_audio_node(target_node, path, drop_pos);
+ }
}
undo_redo->commit_action();
@@ -5994,71 +6046,108 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
}
Vector<String> files = d["files"];
- bool can_instantiate = false;
- bool is_cyclical_dep = false;
- String error_file;
- // Check if at least one of the dragged files is a texture or scene.
- for (int i = 0; i < files.size(); i++) {
- bool is_scene = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "PackedScene");
- bool is_texture = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Texture2D");
+ const Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
+ if (!edited_scene && files.size() > 1) {
+ canvas_item_editor->message = TTR("Can't instantiate multiple nodes without root.");
+ canvas_item_editor->update_viewport();
+ return false;
+ }
+
+ enum {
+ SCENE = 1 << 0,
+ TEXTURE = 1 << 1,
+ AUDIO = 1 << 2,
+ };
+ int instantiate_type = 0;
- if (is_scene || is_texture) {
- Ref<Resource> res = ResourceLoader::load(files[i]);
- if (res.is_null()) {
+ for (const String &path : files) {
+ const String &res_type = ResourceLoader::get_resource_type(path);
+ String error_message;
+
+ if (ClassDB::is_parent_class(res_type, "PackedScene")) {
+ Ref<PackedScene> scn = ResourceLoader::load(path);
+ ERR_CONTINUE(scn.is_null());
+
+ Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
+ if (!instantiated_scene) {
continue;
}
- Ref<PackedScene> scn = res;
- if (scn.is_valid()) {
- Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
- if (!instantiated_scene) {
- continue;
- }
-
- Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
- if (edited_scene && !edited_scene->get_scene_file_path().is_empty() && _cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) {
- memdelete(instantiated_scene);
- can_instantiate = false;
- is_cyclical_dep = true;
- error_file = files[i].get_file();
- break;
- }
- memdelete(instantiated_scene);
+ if (edited_scene && !edited_scene->get_scene_file_path().is_empty() && _cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) {
+ error_message = vformat(TTR("Circular dependency found at %s."), path.get_file());
}
- can_instantiate = true;
+ memdelete(instantiated_scene);
+ instantiate_type |= SCENE;
+ }
+ if (ClassDB::is_parent_class(res_type, "Texture2D")) {
+ instantiate_type |= TEXTURE;
+ }
+ if (ClassDB::is_parent_class(res_type, "AudioStream")) {
+ instantiate_type |= AUDIO;
+ }
+
+ if (!error_message.is_empty()) {
+ // TRANSLATORS: The placeholder is the error message.
+ canvas_item_editor->message = vformat(TTR("Can't instantiate: %s"), error_message);
+ canvas_item_editor->update_viewport();
+ return false;
}
}
- if (is_cyclical_dep) {
- canvas_item_editor->message = vformat(TTR("Can't instantiate: %s."), vformat(TTR("Circular dependency found at %s"), error_file));
- canvas_item_editor->update_viewport();
+ if (instantiate_type == 0) {
return false;
}
- if (can_instantiate) {
- if (!preview_node->get_parent()) { // create preview only once
- _create_preview(files);
- }
- ERR_FAIL_COND_V(preview_node->get_child_count() == 0, false);
- Transform2D trans = canvas_item_editor->get_canvas_transform();
- preview_node->set_position((p_point - trans.get_origin()) / trans.get_scale().x);
+ if (!preview_node->get_parent()) { // create preview only once
+ _create_preview(files);
+ }
+ ERR_FAIL_COND_V(preview_node->get_child_count() == 0, false);
+
+ const Transform2D trans = canvas_item_editor->get_canvas_transform();
+ preview_node->set_position((p_point - trans.get_origin()) / trans.get_scale().x);
+
+ if (!edited_scene && instantiate_type & SCENE) {
String scene_file_path = preview_node->get_child(0)->get_scene_file_path();
- if (scene_file_path.is_empty() || preview_node->get_tree()->get_edited_scene_root()) {
- double snap = EDITOR_GET("interface/inspector/default_float_step");
- int snap_step_decimals = Math::range_step_decimals(snap);
+ // TRANSLATORS: The placeholder is the file path of the scene being instantiated.
+ canvas_item_editor->message = vformat(TTR("Creating inherited scene from: %s"), scene_file_path);
+ } else {
+ double snap = EDITOR_GET("interface/inspector/default_float_step");
+ int snap_step_decimals = Math::range_step_decimals(snap);
#define FORMAT(value) (TS->format_number(String::num(value, snap_step_decimals)))
- Vector2 preview_node_pos = preview_node->get_global_position();
- canvas_item_editor->message = TTR("Instantiating:") + " (" + FORMAT(preview_node_pos.x) + ", " + FORMAT(preview_node_pos.y) + ") px";
- label->set_text(vformat(TTR("Adding %s..."), default_texture_node_type));
- } else {
- canvas_item_editor->message = TTR("Creating inherited scene from: ") + scene_file_path;
+ Vector2 preview_node_pos = preview_node->get_global_position();
+ canvas_item_editor->message = TTR("Instantiating: ") + "(" + FORMAT(preview_node_pos.x) + ", " + FORMAT(preview_node_pos.y) + ") px";
+ }
+ canvas_item_editor->update_viewport();
+
+ if (instantiate_type & TEXTURE && instantiate_type & AUDIO) {
+ // TRANSLATORS: The placeholders are the types of nodes being instantiated.
+ label->set_text(vformat(TTR("Adding %s and %s..."), default_texture_node_type, "AudioStreamPlayer2D"));
+ } else {
+ String node_type;
+ if (instantiate_type & TEXTURE) {
+ node_type = default_texture_node_type;
+ } else if (instantiate_type & AUDIO) {
+ node_type = "AudioStreamPlayer2D";
+ }
+ if (!node_type.is_empty()) {
+ // TRANSLATORS: The placeholder is the type of node being instantiated.
+ label->set_text(vformat(TTR("Adding %s..."), node_type));
}
+ }
+ label->set_visible(instantiate_type & ~SCENE);
- canvas_item_editor->update_viewport();
+ String desc = TTR("Drag and drop to add as sibling of selected node (except when root is selected).") +
+ "\n" + TTR("Hold Shift when dropping to add as child of selected node.") +
+ "\n" + TTR("Hold Alt when dropping to add as child of root node.");
+ if (instantiate_type & TEXTURE) {
+ desc += "\n" + TTR("Hold Alt + Shift when dropping to add as different node type.");
}
- return can_instantiate;
+ label_desc->set_text(desc);
+ label_desc->show();
+
+ return true;
}
-void CanvasItemEditorViewport::_show_resource_type_selector() {
+void CanvasItemEditorViewport::_show_texture_node_type_selector() {
_remove_preview();
List<BaseButton *> btn_list;
button_group->get_buttons(&btn_list);
@@ -6067,18 +6156,17 @@ void CanvasItemEditorViewport::_show_resource_type_selector() {
CheckBox *check = Object::cast_to<CheckBox>(btn);
check->set_pressed(check->get_text() == default_texture_node_type);
}
- selector->set_title(vformat(TTR("Add %s"), default_texture_node_type));
- selector->popup_centered();
+ texture_node_type_selector->set_title(vformat(TTR("Add %s"), default_texture_node_type));
+ texture_node_type_selector->popup_centered();
}
-bool CanvasItemEditorViewport::_only_packed_scenes_selected() const {
+bool CanvasItemEditorViewport::_is_any_texture_selected() const {
for (int i = 0; i < selected_files.size(); ++i) {
- if (ResourceLoader::load(selected_files[i])->get_class() != "PackedScene") {
- return false;
+ if (ClassDB::is_parent_class(ResourceLoader::get_resource_type(selected_files[i]), "Texture2D")) {
+ return true;
}
}
-
- return true;
+ return false;
}
void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p_data) {
@@ -6115,8 +6203,8 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p
drop_pos = p_point;
- if (is_alt && is_shift && !_only_packed_scenes_selected()) {
- _show_resource_type_selector();
+ if (is_alt && is_shift && _is_any_texture_selected()) {
+ _show_texture_node_type_selector();
} else {
_perform_drop_data();
}
@@ -6176,19 +6264,19 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(CanvasItemEditor *p_canvas_it
accept = memnew(AcceptDialog);
EditorNode::get_singleton()->get_gui_base()->add_child(accept);
- selector = memnew(AcceptDialog);
- EditorNode::get_singleton()->get_gui_base()->add_child(selector);
- selector->set_title(TTR("Change Default Type"));
- selector->connect("confirmed", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_confirmed));
- selector->connect("canceled", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_closed));
+ texture_node_type_selector = memnew(AcceptDialog);
+ EditorNode::get_singleton()->get_gui_base()->add_child(texture_node_type_selector);
+ texture_node_type_selector->set_title(TTR("Change Default Type"));
+ texture_node_type_selector->connect("confirmed", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_confirmed));
+ texture_node_type_selector->connect("canceled", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_closed));
VBoxContainer *vbc = memnew(VBoxContainer);
- selector->add_child(vbc);
+ texture_node_type_selector->add_child(vbc);
vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
vbc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vbc->set_custom_minimum_size(Size2(240, 260) * EDSCALE);
- btn_group = memnew(VBoxContainer);
+ VBoxContainer *btn_group = memnew(VBoxContainer);
vbc->add_child(btn_group);
btn_group->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -6197,7 +6285,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(CanvasItemEditor *p_canvas_it
CheckBox *check = memnew(CheckBox);
btn_group->add_child(check);
check->set_text(texture_node_types[i]);
- check->connect("button_down", callable_mp(this, &CanvasItemEditorViewport::_on_select_type).bind(check));
+ check->connect("button_down", callable_mp(this, &CanvasItemEditorViewport::_on_select_texture_node_type).bind(check));
check->set_button_group(button_group);
}
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 6d951a77ec..a9de5e9a0b 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -32,10 +32,11 @@
#define CANVAS_ITEM_EDITOR_PLUGIN_H
#include "editor/plugins/editor_plugin.h"
-#include "scene/gui/base_button.h"
#include "scene/gui/box_container.h"
class AcceptDialog;
+class Button;
+class ButtonGroup;
class CanvasItemEditorViewport;
class ConfirmationDialog;
class EditorData;
@@ -409,7 +410,7 @@ private:
void _selection_result_pressed(int);
void _selection_menu_hide();
void _add_node_pressed(int p_result);
- void _node_created(Node *p_node);
+ void _adjust_new_node_position(Node *p_node);
void _reset_create_position();
void _update_editor_settings();
bool _is_grid_visible() const;
@@ -634,15 +635,13 @@ class CanvasItemEditorViewport : public Control {
CanvasItemEditor *canvas_item_editor = nullptr;
Control *preview_node = nullptr;
AcceptDialog *accept = nullptr;
- AcceptDialog *selector = nullptr;
- Label *selector_label = nullptr;
+ AcceptDialog *texture_node_type_selector = nullptr;
Label *label = nullptr;
Label *label_desc = nullptr;
- VBoxContainer *btn_group = nullptr;
Ref<ButtonGroup> button_group;
void _on_mouse_exit();
- void _on_select_type(Object *selected);
+ void _on_select_texture_node_type(Object *selected);
void _on_change_type_confirmed();
void _on_change_type_closed();
@@ -650,11 +649,12 @@ class CanvasItemEditorViewport : public Control {
void _remove_preview();
bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) const;
- bool _only_packed_scenes_selected() const;
- void _create_nodes(Node *parent, Node *child, String &path, const Point2 &p_point);
- bool _create_instance(Node *parent, String &path, const Point2 &p_point);
+ bool _is_any_texture_selected() const;
+ void _create_texture_node(Node *p_parent, Node *p_child, const String &p_path, const Point2 &p_point);
+ void _create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point);
+ bool _create_instance(Node *p_parent, const String &p_path, const Point2 &p_point);
void _perform_drop_data();
- void _show_resource_type_selector();
+ void _show_texture_node_type_selector();
void _update_theme();
protected:
diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
index dfc8323fc0..1d53a1b4d4 100644
--- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
@@ -33,6 +33,7 @@
#include "canvas_item_editor_plugin.h"
#include "core/io/image_loader.h"
#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/scene_tree_dock.h"
@@ -268,7 +269,7 @@ CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin() {
toolbar->hide();
menu = memnew(MenuButton);
- menu->get_popup()->add_item(TTR("Restart"), MENU_RESTART);
+ menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_RESTART);
menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK);
menu->get_popup()->add_item(TTR("Convert to GPUParticles2D"), MENU_CONVERT_TO_GPU_PARTICLES);
menu->set_text(TTR("CPUParticles2D"));
diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.cpp b/editor/plugins/cpu_particles_3d_editor_plugin.cpp
index b5e3f102cf..baf70e45f0 100644
--- a/editor/plugins/cpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/cpu_particles_3d_editor_plugin.cpp
@@ -31,6 +31,7 @@
#include "cpu_particles_3d_editor_plugin.h"
#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/scene_tree_editor.h"
#include "editor/plugins/node_3d_editor_plugin.h"
@@ -168,7 +169,7 @@ CPUParticles3DEditor::CPUParticles3DEditor() {
particles_editor_hb->hide();
options->set_text(TTR("CPUParticles3D"));
- options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_OPTION_RESTART);
options->get_popup()->add_item(TTR("Generate AABB"), MENU_OPTION_GENERATE_AABB);
options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE);
options->get_popup()->add_item(TTR("Convert to GPUParticles3D"), MENU_OPTION_CONVERT_TO_GPU_PARTICLES);
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index e9f1b07c34..328b272562 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -33,6 +33,7 @@
#include "canvas_item_editor_plugin.h"
#include "core/io/image_loader.h"
#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/scene_tree_dock.h"
@@ -370,7 +371,7 @@ GPUParticles2DEditorPlugin::GPUParticles2DEditorPlugin() {
toolbar->hide();
menu = memnew(MenuButton);
- menu->get_popup()->add_item(TTR("Restart"), MENU_RESTART);
+ menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_RESTART);
menu->get_popup()->add_item(TTR("Generate Visibility Rect"), MENU_GENERATE_VISIBILITY_RECT);
menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK);
// menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK);
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index 04b0a8aa26..9063109ece 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -32,6 +32,7 @@
#include "core/io/resource_loader.h"
#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "editor/scene_tree_dock.h"
@@ -414,7 +415,7 @@ GPUParticles3DEditor::GPUParticles3DEditor() {
particles_editor_hb->hide();
options->set_text(TTR("GPUParticles3D"));
- options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_OPTION_RESTART);
options->get_popup()->add_item(TTR("Generate AABB"), MENU_OPTION_GENERATE_AABB);
options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE);
options->get_popup()->add_item(TTR("Convert to CPUParticles3D"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index ca7ea821e8..69b66cd7b2 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -78,12 +78,14 @@
#include "editor/plugins/gizmos/voxel_gi_gizmo_plugin.h"
#include "editor/plugins/node_3d_editor_gizmos.h"
#include "editor/scene_tree_dock.h"
+#include "scene/3d/audio_stream_player_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/decal.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/physics/collision_shape_3d.h"
#include "scene/3d/physics/physics_body_3d.h"
+#include "scene/3d/sprite_3d.h"
#include "scene/3d/visual_instance_3d.h"
#include "scene/3d/world_environment.h"
#include "scene/gui/center_container.h"
@@ -737,9 +739,21 @@ void Node3DEditorViewport::_select_clicked(bool p_allow_locked) {
return;
}
+ Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
+
+ // Prevent selection of nodes not owned by the edited scene.
+ while (node && node != edited_scene->get_parent()) {
+ Node *node_owner = node->get_owner();
+ if (node_owner == edited_scene || node == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
+ break;
+ }
+ node = node->get_parent();
+ selected = Object::cast_to<Node3D>(node);
+ }
+
if (!p_allow_locked) {
// Replace the node by the group if grouped
- while (node && node != EditorNode::get_singleton()->get_edited_scene()->get_parent()) {
+ while (node && node != edited_scene->get_parent()) {
Node3D *selected_tmp = Object::cast_to<Node3D>(node);
if (selected_tmp && node->has_meta("_edit_group_")) {
selected = selected_tmp;
@@ -1042,25 +1056,34 @@ void Node3DEditorViewport::_select_region() {
found_nodes.insert(sp);
- Node *item = Object::cast_to<Node>(sp);
- if (item != edited_scene) {
- item = edited_scene->get_deepest_editable_node(item);
+ Node *node = Object::cast_to<Node>(sp);
+ if (node != edited_scene) {
+ node = edited_scene->get_deepest_editable_node(node);
+ }
+
+ // Prevent selection of nodes not owned by the edited scene.
+ while (node && node != edited_scene->get_parent()) {
+ Node *node_owner = node->get_owner();
+ if (node_owner == edited_scene || node == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
+ break;
+ }
+ node = node->get_parent();
}
// Replace the node by the group if grouped
- if (item->is_class("Node3D")) {
- Node3D *sel = Object::cast_to<Node3D>(item);
- while (item && item != EditorNode::get_singleton()->get_edited_scene()->get_parent()) {
- Node3D *selected_tmp = Object::cast_to<Node3D>(item);
- if (selected_tmp && item->has_meta("_edit_group_")) {
+ if (node->is_class("Node3D")) {
+ Node3D *sel = Object::cast_to<Node3D>(node);
+ while (node && node != EditorNode::get_singleton()->get_edited_scene()->get_parent()) {
+ Node3D *selected_tmp = Object::cast_to<Node3D>(node);
+ if (selected_tmp && node->has_meta("_edit_group_")) {
sel = selected_tmp;
}
- item = item->get_parent();
+ node = node->get_parent();
}
- item = sel;
+ node = sel;
}
- if (_is_node_locked(item)) {
+ if (_is_node_locked(node)) {
continue;
}
@@ -1072,7 +1095,7 @@ void Node3DEditorViewport::_select_region() {
}
if (seg->intersect_frustum(camera, frustum)) {
- selected.push_back(item);
+ selected.push_back(node);
}
}
}
@@ -1529,23 +1552,35 @@ bool Node3DEditorViewport ::_is_node_locked(const Node *p_node) {
}
void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
- _find_items_at_pos(b->get_position(), selection_results, spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT);
+ Vector<_RayResult> potential_selection_results;
+ _find_items_at_pos(b->get_position(), potential_selection_results, spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT);
- Node *scene = EditorNode::get_singleton()->get_edited_scene();
+ Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
- for (int i = 0; i < selection_results.size(); i++) {
- Node3D *item = selection_results[i].item;
- if (item != scene && item->get_owner() != scene && item != scene->get_deepest_editable_node(item)) {
- //invalid result
- selection_results.remove_at(i);
- i--;
+ // Filter to a list of nodes which include either the edited scene or nodes directly owned by the edited scene.
+ // If a node has an invalid owner, recursively check their parents until a valid node is found.
+ for (int i = 0; i < potential_selection_results.size(); i++) {
+ Node3D *node = potential_selection_results[i].item;
+ while (true) {
+ if (node == nullptr || node == edited_scene->get_parent()) {
+ break;
+ } else {
+ Node *node_owner = node->get_owner();
+ if (node == edited_scene || node_owner == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
+ if (selection_results.has(node)) {
+ selection_results.append(node);
+ }
+ break;
+ }
+ }
+ node = Object::cast_to<Node3D>(node->get_parent());
}
}
clicked_wants_append = b->is_shift_pressed();
if (selection_results.size() == 1) {
- clicked = selection_results[0].item->get_instance_id();
+ clicked = selection_results[0]->get_instance_id();
selection_results.clear();
if (clicked.is_valid()) {
@@ -1556,7 +1591,7 @@ void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
StringName root_name = root_path.get_name(root_path.get_name_count() - 1);
for (int i = 0; i < selection_results.size(); i++) {
- Node3D *spat = selection_results[i].item;
+ Node3D *spat = selection_results[i];
Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(spat, "Node");
@@ -2899,10 +2934,8 @@ void Node3DEditorViewport::_notification(int p_what) {
// FPS Counter.
bool show_fps = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FRAME_TIME));
- if (show_fps != fps_label->is_visible()) {
- cpu_time_label->set_visible(show_fps);
- gpu_time_label->set_visible(show_fps);
- fps_label->set_visible(show_fps);
+ if (show_fps != frame_time_panel->is_visible()) {
+ frame_time_panel->set_visible(show_fps);
RS::get_singleton()->viewport_set_measure_render_time(viewport->get_viewport_rid(), show_fps);
for (int i = 0; i < FRAME_TIME_HISTORY; i++) {
// Initialize to 120 FPS, so that the initial estimation until we get enough data is always reasonable.
@@ -3031,9 +3064,15 @@ void Node3DEditorViewport::_notification(int p_what) {
frame_time_gradient->set_color(2, get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
info_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
- cpu_time_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
- gpu_time_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
- fps_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
+
+ frame_time_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
+ // Set a minimum width to prevent the width from changing all the time
+ // when numbers vary rapidly. This minimum width is set based on a
+ // GPU time of 999.99 ms in the current editor language.
+ const float min_width = get_theme_font(SNAME("main"), EditorStringName(EditorFonts))->get_string_size(vformat(TTR("GPU Time: %s ms"), 999.99)).x;
+ frame_time_panel->set_custom_minimum_size(Size2(min_width, 0) * EDSCALE);
+ frame_time_vbox->add_theme_constant_override("separation", Math::round(-1 * EDSCALE));
+
cinema_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
locked_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
} break;
@@ -3748,7 +3787,7 @@ void Node3DEditorViewport::_selection_result_pressed(int p_result) {
return;
}
- clicked = selection_results_menu[p_result].item->get_instance_id();
+ clicked = selection_results_menu[p_result]->get_instance_id();
if (clicked.is_valid()) {
_select_clicked(spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT);
@@ -4192,27 +4231,37 @@ Node *Node3DEditorViewport::_sanitize_preview_node(Node *p_node) const {
void Node3DEditorViewport::_create_preview_node(const Vector<String> &files) const {
bool add_preview = false;
- for (int i = 0; i < files.size(); i++) {
- Ref<Resource> res = ResourceLoader::load(files[i]);
+ for (const String &path : files) {
+ Ref<Resource> res = ResourceLoader::load(path);
ERR_CONTINUE(res.is_null());
- Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
- Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res));
- if (mesh != nullptr || scene != nullptr) {
- if (mesh != nullptr) {
- MeshInstance3D *mesh_instance = memnew(MeshInstance3D);
- mesh_instance->set_mesh(mesh);
- preview_node->add_child(mesh_instance);
- } else {
- if (scene.is_valid()) {
- Node *instance = scene->instantiate();
- if (instance) {
- instance = _sanitize_preview_node(instance);
- preview_node->add_child(instance);
- }
- }
+
+ Ref<PackedScene> scene = res;
+ if (scene.is_valid()) {
+ Node *instance = scene->instantiate();
+ if (instance) {
+ instance = _sanitize_preview_node(instance);
+ preview_node->add_child(instance);
}
add_preview = true;
}
+
+ Ref<Mesh> mesh = res;
+ if (mesh.is_valid()) {
+ MeshInstance3D *mesh_instance = memnew(MeshInstance3D);
+ mesh_instance->set_mesh(mesh);
+ preview_node->add_child(mesh_instance);
+ add_preview = true;
+ }
+
+ Ref<AudioStream> audio = res;
+ if (audio.is_valid()) {
+ Sprite3D *sprite = memnew(Sprite3D);
+ sprite->set_texture(get_editor_theme_icon(SNAME("Gizmo3DSamplePlayer")));
+ sprite->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED);
+ sprite->set_pixel_size(0.005);
+ preview_node->add_child(sprite);
+ add_preview = true;
+ }
}
if (add_preview) {
EditorNode::get_singleton()->get_scene_root()->add_child(preview_node);
@@ -4346,12 +4395,12 @@ bool Node3DEditorViewport::_cyclical_dependency_exists(const String &p_target_sc
return false;
}
-bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) {
- Ref<Resource> res = ResourceLoader::load(path);
+bool Node3DEditorViewport::_create_instance(Node *p_parent, const String &p_path, const Point2 &p_point) {
+ Ref<Resource> res = ResourceLoader::load(p_path);
ERR_FAIL_COND_V(res.is_null(), false);
- Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
- Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res));
+ Ref<PackedScene> scene = res;
+ Ref<Mesh> mesh = res;
Node *instantiated_scene = nullptr;
@@ -4361,8 +4410,10 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
mesh_instance->set_mesh(mesh);
// Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
- String name = path.get_file().get_basename();
- mesh_instance->set_name(Node::adjust_name_casing(name));
+ const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
+ if (!node_name.is_empty()) {
+ mesh_instance->set_name(node_name);
+ }
instantiated_scene = mesh_instance;
} else {
@@ -4386,25 +4437,25 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
}
if (scene != nullptr) {
- instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
+ instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(p_path));
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->add_do_method(parent, "add_child", instantiated_scene, true);
+ undo_redo->add_do_method(p_parent, "add_child", instantiated_scene, true);
undo_redo->add_do_method(instantiated_scene, "set_owner", EditorNode::get_singleton()->get_edited_scene());
undo_redo->add_do_reference(instantiated_scene);
- undo_redo->add_undo_method(parent, "remove_child", instantiated_scene);
+ undo_redo->add_undo_method(p_parent, "remove_child", instantiated_scene);
undo_redo->add_do_method(editor_selection, "add_node", instantiated_scene);
- String new_name = parent->validate_child_name(instantiated_scene);
+ String new_name = p_parent->validate_child_name(instantiated_scene);
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
- undo_redo->add_do_method(ed, "live_debug_instantiate_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), path, new_name);
- undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
+ undo_redo->add_do_method(ed, "live_debug_instantiate_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), p_path, new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
Node3D *node3d = Object::cast_to<Node3D>(instantiated_scene);
if (node3d) {
Transform3D parent_tf;
- Node3D *parent_node3d = Object::cast_to<Node3D>(parent);
+ Node3D *parent_node3d = Object::cast_to<Node3D>(p_parent);
if (parent_node3d) {
parent_tf = parent_node3d->get_global_gizmo_transform();
}
@@ -4419,6 +4470,46 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
return true;
}
+bool Node3DEditorViewport::_create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point) {
+ Ref<AudioStream> audio = ResourceLoader::load(p_path);
+ ERR_FAIL_COND_V(audio.is_null(), false);
+
+ AudioStreamPlayer3D *audio_player = memnew(AudioStreamPlayer3D);
+ audio_player->set_stream(audio);
+
+ // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
+ const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
+ if (!node_name.is_empty()) {
+ audio_player->set_name(node_name);
+ }
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->add_do_method(p_parent, "add_child", audio_player, true);
+ undo_redo->add_do_method(audio_player, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->add_do_reference(audio_player);
+ undo_redo->add_undo_method(p_parent, "remove_child", audio_player);
+ undo_redo->add_do_method(editor_selection, "add_node", audio_player);
+
+ const String new_name = p_parent->validate_child_name(audio_player);
+ EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
+ undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), audio_player->get_class(), new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
+
+ Transform3D parent_tf;
+ Node3D *parent_node3d = Object::cast_to<Node3D>(p_parent);
+ if (parent_node3d) {
+ parent_tf = parent_node3d->get_global_gizmo_transform();
+ }
+
+ Transform3D new_tf = audio_player->get_transform();
+ new_tf.origin = parent_tf.affine_inverse().xform(preview_node_pos + audio_player->get_position());
+ new_tf.basis = parent_tf.affine_inverse().basis * new_tf.basis;
+
+ undo_redo->add_do_method(audio_player, "set_transform", new_tf);
+
+ return true;
+}
+
void Node3DEditorViewport::_perform_drop_data() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (spatial_editor->get_preview_material_target().is_valid()) {
@@ -4453,11 +4544,18 @@ void Node3DEditorViewport::_perform_drop_data() {
if (res.is_null()) {
continue;
}
- Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
- Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res));
- if (mesh != nullptr || scene != nullptr) {
- bool success = _create_instance(target_node, path, drop_pos);
- if (!success) {
+
+ Ref<PackedScene> scene = res;
+ Ref<Mesh> mesh = res;
+ if (mesh.is_valid() || scene.is_valid()) {
+ if (!_create_instance(target_node, path, drop_pos)) {
+ error_files.push_back(path.get_file());
+ }
+ }
+
+ Ref<AudioStream> audio = res;
+ if (audio.is_valid()) {
+ if (!_create_audio_node(target_node, path, drop_pos)) {
error_files.push_back(path.get_file());
}
}
@@ -4488,12 +4586,14 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant
bool is_other_valid = false;
// Check if at least one of the dragged files is a mesh, material, texture or scene.
for (int i = 0; i < files.size(); i++) {
- bool is_scene = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "PackedScene");
- bool is_mesh = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Mesh");
- bool is_material = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Material");
- bool is_texture = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Texture");
-
- if (is_mesh || is_scene || is_material || is_texture) {
+ const String &res_type = ResourceLoader::get_resource_type(files[i]);
+ bool is_scene = ClassDB::is_parent_class(res_type, "PackedScene");
+ bool is_mesh = ClassDB::is_parent_class(res_type, "Mesh");
+ bool is_material = ClassDB::is_parent_class(res_type, "Material");
+ bool is_texture = ClassDB::is_parent_class(res_type, "Texture");
+ bool is_audio = ClassDB::is_parent_class(res_type, "AudioStream");
+
+ if (is_mesh || is_scene || is_material || is_texture || is_audio) {
Ref<Resource> res = ResourceLoader::load(files[i]);
if (res.is_null()) {
continue;
@@ -4502,6 +4602,7 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant
Ref<Mesh> mesh = res;
Ref<Material> mat = res;
Ref<Texture2D> tex = res;
+ Ref<AudioStream> audio = res;
if (scn.is_valid()) {
Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
if (!instantiated_scene) {
@@ -4537,6 +4638,8 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant
spatial_editor->set_preview_material(new_mat);
is_other_valid = true;
continue;
+ } else if (!is_other_valid && audio.is_valid()) {
+ is_other_valid = true;
} else {
continue;
}
@@ -5313,10 +5416,6 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
top_right_vbox = memnew(VBoxContainer);
top_right_vbox->set_anchors_and_offsets_preset(PRESET_TOP_RIGHT, PRESET_MODE_MINSIZE, 10.0 * EDSCALE);
top_right_vbox->set_h_grow_direction(GROW_DIRECTION_BEGIN);
- // Make sure frame time labels don't touch the viewport's edge.
- top_right_vbox->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
- // Prevent visible spacing between frame time labels.
- top_right_vbox->add_theme_constant_override("separation", 0);
const int navigation_control_size = 150;
@@ -5348,18 +5447,22 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
rotation_control->set_viewport(this);
top_right_vbox->add_child(rotation_control);
+ frame_time_panel = memnew(PanelContainer);
+ top_right_vbox->add_child(frame_time_panel);
+ frame_time_panel->hide();
+
+ frame_time_vbox = memnew(VBoxContainer);
+ frame_time_panel->add_child(frame_time_vbox);
+
// Individual Labels are used to allow coloring each label with its own color.
cpu_time_label = memnew(Label);
- top_right_vbox->add_child(cpu_time_label);
- cpu_time_label->hide();
+ frame_time_vbox->add_child(cpu_time_label);
gpu_time_label = memnew(Label);
- top_right_vbox->add_child(gpu_time_label);
- gpu_time_label->hide();
+ frame_time_vbox->add_child(gpu_time_label);
fps_label = memnew(Label);
- top_right_vbox->add_child(fps_label);
- fps_label->hide();
+ frame_time_vbox->add_child(fps_label);
surface->add_child(top_right_vbox);
@@ -8410,7 +8513,7 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_LOCK_SELECTED]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_LOCK_SELECTED));
tool_button[TOOL_LOCK_SELECTED]->set_tooltip_text(TTR("Lock selected node, preventing selection and movement."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- tool_button[TOOL_LOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::L));
+ tool_button[TOOL_LOCK_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/lock_selected_nodes"));
tool_button[TOOL_UNLOCK_SELECTED] = memnew(Button);
main_menu_hbox->add_child(tool_button[TOOL_UNLOCK_SELECTED]);
@@ -8418,7 +8521,7 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_UNLOCK_SELECTED]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_UNLOCK_SELECTED));
tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip_text(TTR("Unlock selected node, allowing selection and movement."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- tool_button[TOOL_UNLOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::L));
+ tool_button[TOOL_UNLOCK_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/unlock_selected_nodes"));
tool_button[TOOL_GROUP_SELECTED] = memnew(Button);
main_menu_hbox->add_child(tool_button[TOOL_GROUP_SELECTED]);
@@ -8426,7 +8529,7 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_GROUP_SELECTED]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_GROUP_SELECTED));
tool_button[TOOL_GROUP_SELECTED]->set_tooltip_text(TTR("Groups the selected node with its children. This selects the parent when any child node is clicked in 2D and 3D view."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- tool_button[TOOL_GROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::G));
+ tool_button[TOOL_GROUP_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/group_selected_nodes"));
tool_button[TOOL_UNGROUP_SELECTED] = memnew(Button);
main_menu_hbox->add_child(tool_button[TOOL_UNGROUP_SELECTED]);
@@ -8434,7 +8537,7 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_UNGROUP_SELECTED]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_UNGROUP_SELECTED));
tool_button[TOOL_UNGROUP_SELECTED]->set_tooltip_text(TTR("Ungroups the selected node from its children. Child nodes will be individual items in 2D and 3D view."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- tool_button[TOOL_UNGROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::G));
+ tool_button[TOOL_UNGROUP_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/ungroup_selected_nodes"));
main_menu_hbox->add_child(memnew(VSeparator));
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index a3e1224cb8..859d075732 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -255,6 +255,8 @@ private:
ViewportNavigationControl *look_control = nullptr;
ViewportRotationControl *rotation_control = nullptr;
Gradient *frame_time_gradient = nullptr;
+ PanelContainer *frame_time_panel = nullptr;
+ VBoxContainer *frame_time_vbox = nullptr;
Label *cpu_time_label = nullptr;
Label *gpu_time_label = nullptr;
Label *fps_label = nullptr;
@@ -296,8 +298,8 @@ private:
ObjectID clicked;
ObjectID material_target;
- Vector<_RayResult> selection_results;
- Vector<_RayResult> selection_results_menu;
+ Vector<Node3D *> selection_results;
+ Vector<Node3D *> selection_results_menu;
bool clicked_wants_append = false;
bool selection_in_progress = false;
@@ -443,7 +445,8 @@ private:
void _reset_preview_material() const;
void _remove_preview_material();
bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) const;
- bool _create_instance(Node *parent, String &path, const Point2 &p_point);
+ bool _create_instance(Node *p_parent, const String &p_path, const Point2 &p_point);
+ bool _create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point);
void _perform_drop_data();
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index eb6282ca0c..0a0909ec9f 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -765,7 +765,7 @@ void ScriptEditor::_update_recent_scripts() {
}
recent_scripts->add_separator();
- recent_scripts->add_shortcut(ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Files")));
+ recent_scripts->add_shortcut(ED_GET_SHORTCUT("script_editor/clear_recent"));
recent_scripts->set_item_disabled(recent_scripts->get_item_id(recent_scripts->get_item_count() - 1), rc.is_empty());
recent_scripts->reset_size();
@@ -3591,13 +3591,13 @@ void ScriptEditor::_update_selected_editor_menu() {
script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), Key::F3), HELP_SEARCH_FIND_NEXT);
script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KeyModifierMask::SHIFT | Key::F3), HELP_SEARCH_FIND_PREVIOUS);
script_search_menu->get_popup()->add_separator();
- script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F), SEARCH_IN_FILES);
- script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/replace_in_files", TTR("Replace in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::R), REPLACE_IN_FILES);
+ script_search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/find_in_files"), SEARCH_IN_FILES);
+ script_search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/replace_in_files"), REPLACE_IN_FILES);
script_search_menu->show();
} else {
if (tab_container->get_tab_count() == 0) {
- script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F), SEARCH_IN_FILES);
- script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/replace_in_files", TTR("Replace in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::R), REPLACE_IN_FILES);
+ script_search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/find_in_files"), SEARCH_IN_FILES);
+ script_search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/replace_in_files"), REPLACE_IN_FILES);
script_search_menu->show();
} else {
script_search_menu->hide();
@@ -4101,7 +4101,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New Script..."), KeyModifierMask::CMD_OR_CTRL | Key::N), FILE_NEW);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new_textfile", TTR("New Text File..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::N), FILE_NEW_TEXTFILE);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open...")), FILE_OPEN);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::T), FILE_REOPEN_CLOSED);
+ file_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/reopen_closed_script"), FILE_REOPEN_CLOSED);
recent_scripts = memnew(PopupMenu);
file_menu->get_popup()->add_submenu_node_item(TTR("Open Recent"), recent_scripts, FILE_OPEN_RECENT);
@@ -4122,6 +4122,9 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Previous"), KeyModifierMask::ALT | Key::LEFT), WINDOW_PREV);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KeyModifierMask::ALT | Key::RIGHT), WINDOW_NEXT);
+ ED_SHORTCUT_OVERRIDE("script_editor/history_previous", "macos", KeyModifierMask::ALT | KeyModifierMask::META | Key::LEFT);
+ ED_SHORTCUT_OVERRIDE("script_editor/history_next", "macos", KeyModifierMask::ALT | KeyModifierMask::META | Key::RIGHT);
+
file_menu->get_popup()->add_separator();
theme_submenu = memnew(PopupMenu);
@@ -4246,12 +4249,18 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
disk_changed = memnew(ConfirmationDialog);
{
+ disk_changed->set_title(TTR("Files have been modified on disk"));
+
VBoxContainer *vbc = memnew(VBoxContainer);
disk_changed->add_child(vbc);
- Label *dl = memnew(Label);
- dl->set_text(TTR("The following files are newer on disk.\nWhat action should be taken?:"));
- vbc->add_child(dl);
+ Label *files_are_newer_label = memnew(Label);
+ files_are_newer_label->set_text(TTR("The following files are newer on disk."));
+ vbc->add_child(files_are_newer_label);
+
+ Label *what_action_label = memnew(Label);
+ what_action_label->set_text(TTR("What action should be taken?:"));
+ vbc->add_child(what_action_label);
disk_changed_list = memnew(Tree);
vbc->add_child(disk_changed_list);
@@ -4259,9 +4268,9 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
disk_changed_list->set_v_size_flags(SIZE_EXPAND_FILL);
disk_changed->connect("confirmed", callable_mp(this, &ScriptEditor::reload_scripts).bind(false));
- disk_changed->set_ok_button_text(TTR("Reload"));
+ disk_changed->set_ok_button_text(TTR("Discard local changes and reload"));
- disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
+ disk_changed->add_button(TTR("Keep local changes and overwrite"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
disk_changed->connect("custom_action", callable_mp(this, &ScriptEditor::_resave_scripts));
}
@@ -4499,6 +4508,15 @@ void ScriptEditorPlugin::edited_scene_changed() {
}
ScriptEditorPlugin::ScriptEditorPlugin() {
+ ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::T);
+ ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Scripts"));
+ ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F);
+ ED_SHORTCUT("script_editor/replace_in_files", TTR("Replace in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::R);
+
+ ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KeyModifierMask::SHIFT | Key::F4);
+ ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase"), KeyModifierMask::SHIFT | Key::F5);
+ ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KeyModifierMask::SHIFT | Key::F6);
+
window_wrapper = memnew(WindowWrapper);
window_wrapper->set_window_title(vformat(TTR("%s - Godot Engine"), TTR("Script Editor")));
window_wrapper->set_margins_enabled(true);
@@ -4527,9 +4545,6 @@ ScriptEditorPlugin::ScriptEditorPlugin() {
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_path", PROPERTY_HINT_GLOBAL_FILE));
EDITOR_DEF("text_editor/external/exec_flags", "{file}");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_flags", PROPERTY_HINT_PLACEHOLDER_TEXT, "Call flags with placeholders: {project}, {file}, {col}, {line}."));
-
- ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::T);
- ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Scripts"));
}
ScriptEditorPlugin::~ScriptEditorPlugin() {
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 191d58228b..a5d89ff54c 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -2133,10 +2133,11 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
void ScriptTextEditor::_color_changed(const Color &p_color) {
String new_args;
+ const int decimals = 3;
if (p_color.a == 1.0f) {
- new_args = String("(" + rtos(p_color.r) + ", " + rtos(p_color.g) + ", " + rtos(p_color.b) + ")");
+ new_args = String("(" + String::num(p_color.r, decimals) + ", " + String::num(p_color.g, decimals) + ", " + String::num(p_color.b, decimals) + ")");
} else {
- new_args = String("(" + rtos(p_color.r) + ", " + rtos(p_color.g) + ", " + rtos(p_color.b) + ", " + rtos(p_color.a) + ")");
+ new_args = String("(" + String::num(p_color.r, decimals) + ", " + String::num(p_color.g, decimals) + ", " + String::num(p_color.b, decimals) + ", " + String::num(p_color.a, decimals) + ")");
}
String line = code_editor->get_text_editor()->get_line(color_position.x);
@@ -2311,9 +2312,9 @@ void ScriptTextEditor::_enable_code_editor() {
edit_menu->get_popup()->add_separator();
{
PopupMenu *sub_menu = memnew(PopupMenu);
- sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KeyModifierMask::SHIFT | Key::F4), EDIT_TO_UPPERCASE);
- sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase"), KeyModifierMask::SHIFT | Key::F5), EDIT_TO_LOWERCASE);
- sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KeyModifierMask::SHIFT | Key::F6), EDIT_CAPITALIZE);
+ sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_uppercase"), EDIT_TO_UPPERCASE);
+ sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_lowercase"), EDIT_TO_LOWERCASE);
+ sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/capitalize"), EDIT_CAPITALIZE);
sub_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
edit_menu->get_popup()->add_submenu_node_item(TTR("Convert Case"), sub_menu);
}
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 56f8e1173f..c14336418c 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -2181,6 +2181,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_h->set_min(1);
split_sheet_h->set_max(128);
split_sheet_h->set_step(1);
+ split_sheet_h->set_select_all_on_focus(true);
split_sheet_h_hb->add_child(split_sheet_h);
split_sheet_h->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_FRAME_COUNT));
split_sheet_settings_vb->add_child(split_sheet_h_hb);
@@ -2197,6 +2198,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_v->set_min(1);
split_sheet_v->set_max(128);
split_sheet_v->set_step(1);
+ split_sheet_v->set_select_all_on_focus(true);
split_sheet_v_hb->add_child(split_sheet_v);
split_sheet_v->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_FRAME_COUNT));
split_sheet_settings_vb->add_child(split_sheet_v_hb);
@@ -2216,6 +2218,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_size_x->set_min(1);
split_sheet_size_x->set_step(1);
split_sheet_size_x->set_suffix("px");
+ split_sheet_size_x->set_select_all_on_focus(true);
split_sheet_size_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_SIZE));
split_sheet_size_vb->add_child(split_sheet_size_x);
split_sheet_size_y = memnew(SpinBox);
@@ -2223,6 +2226,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_size_y->set_min(1);
split_sheet_size_y->set_step(1);
split_sheet_size_y->set_suffix("px");
+ split_sheet_size_y->set_select_all_on_focus(true);
split_sheet_size_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_SIZE));
split_sheet_size_vb->add_child(split_sheet_size_y);
split_sheet_size_hb->add_child(split_sheet_size_vb);
@@ -2242,12 +2246,14 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_sep_x->set_min(0);
split_sheet_sep_x->set_step(1);
split_sheet_sep_x->set_suffix("px");
+ split_sheet_sep_x->set_select_all_on_focus(true);
split_sheet_sep_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT));
split_sheet_sep_vb->add_child(split_sheet_sep_x);
split_sheet_sep_y = memnew(SpinBox);
split_sheet_sep_y->set_min(0);
split_sheet_sep_y->set_step(1);
split_sheet_sep_y->set_suffix("px");
+ split_sheet_sep_y->set_select_all_on_focus(true);
split_sheet_sep_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT));
split_sheet_sep_vb->add_child(split_sheet_sep_y);
split_sheet_sep_hb->add_child(split_sheet_sep_vb);
@@ -2267,12 +2273,14 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_offset_x->set_min(0);
split_sheet_offset_x->set_step(1);
split_sheet_offset_x->set_suffix("px");
+ split_sheet_offset_x->set_select_all_on_focus(true);
split_sheet_offset_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT));
split_sheet_offset_vb->add_child(split_sheet_offset_x);
split_sheet_offset_y = memnew(SpinBox);
split_sheet_offset_y->set_min(0);
split_sheet_offset_y->set_step(1);
split_sheet_offset_y->set_suffix("px");
+ split_sheet_offset_y->set_select_all_on_focus(true);
split_sheet_offset_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT));
split_sheet_offset_vb->add_child(split_sheet_offset_y);
split_sheet_offset_hb->add_child(split_sheet_offset_vb);
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 2aa04d7874..5bc3cafe19 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -647,9 +647,9 @@ TextEditor::TextEditor() {
edit_menu->get_popup()->add_separator();
PopupMenu *convert_case = memnew(PopupMenu);
edit_menu->get_popup()->add_submenu_node_item(TTR("Convert Case"), convert_case);
- convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase")), EDIT_TO_UPPERCASE);
- convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase")), EDIT_TO_LOWERCASE);
- convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize")), EDIT_CAPITALIZE);
+ convert_case->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_uppercase"), EDIT_TO_UPPERCASE);
+ convert_case->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_lowercase"), EDIT_TO_LOWERCASE);
+ convert_case->add_shortcut(ED_GET_SHORTCUT("script_text_editor/capitalize"), EDIT_CAPITALIZE);
convert_case->connect("id_pressed", callable_mp(this, &TextEditor::_edit_option));
highlighter_menu = memnew(PopupMenu);
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index bb74bf8d1f..d6cef3ccb9 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -30,12 +30,12 @@
#include "text_shader_editor.h"
+#include "core/config/project_settings.h"
#include "core/version_generated.gen.h"
+#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
-#include "editor/filesystem_dock.h"
-#include "editor/project_settings_editor.h"
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
#include "scene/gui/split_container.h"
@@ -735,6 +735,13 @@ void TextShaderEditor::_menu_option(int p_option) {
}
}
+void TextShaderEditor::_prepare_edit_menu() {
+ const CodeEdit *tx = code_editor->get_text_editor();
+ PopupMenu *popup = edit_menu->get_popup();
+ popup->set_item_disabled(popup->get_item_index(EDIT_UNDO), !tx->has_undo());
+ popup->set_item_disabled(popup->get_item_index(EDIT_REDO), !tx->has_redo());
+}
+
void TextShaderEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
@@ -1088,6 +1095,9 @@ void TextShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position)
context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE);
+ context_menu->set_item_disabled(context_menu->get_item_index(EDIT_UNDO), !code_editor->get_text_editor()->has_undo());
+ context_menu->set_item_disabled(context_menu->get_item_index(EDIT_REDO), !code_editor->get_text_editor()->has_redo());
+
context_menu->set_position(get_screen_position() + p_position);
context_menu->reset_size();
context_menu->popup();
@@ -1128,6 +1138,7 @@ TextShaderEditor::TextShaderEditor() {
edit_menu->set_shortcut_context(this);
edit_menu->set_text(TTR("Edit"));
edit_menu->set_switch_on_hover(true);
+ edit_menu->connect("about_to_popup", callable_mp(this, &TextShaderEditor::_prepare_edit_menu));
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO);
diff --git a/editor/plugins/text_shader_editor.h b/editor/plugins/text_shader_editor.h
index be16148744..6d2ac743b8 100644
--- a/editor/plugins/text_shader_editor.h
+++ b/editor/plugins/text_shader_editor.h
@@ -34,7 +34,6 @@
#include "editor/code_editor.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/menu_button.h"
-#include "scene/gui/panel_container.h"
#include "scene/gui/rich_text_label.h"
#include "servers/rendering/shader_warnings.h"
@@ -153,6 +152,7 @@ class TextShaderEditor : public MarginContainer {
bool compilation_success = true;
void _menu_option(int p_option);
+ void _prepare_edit_menu();
mutable Ref<Shader> shader;
mutable Ref<ShaderInclude> shader_inc;
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 2ee0dc0316..52b58b74a3 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -59,6 +59,9 @@ void TileAtlasView::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<In
}
Size2i TileAtlasView::_compute_base_tiles_control_size() {
+ if (tile_set_atlas_source.is_null()) {
+ return Size2i();
+ }
// Update the texture.
Vector2i size;
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
@@ -69,6 +72,9 @@ Size2i TileAtlasView::_compute_base_tiles_control_size() {
}
Size2i TileAtlasView::_compute_alternative_tiles_control_size() {
+ if (tile_set_atlas_source.is_null()) {
+ return Size2i();
+ }
Vector2i size;
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
@@ -89,6 +95,9 @@ Size2i TileAtlasView::_compute_alternative_tiles_control_size() {
}
void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) {
+ if (tile_set_atlas_source.is_null()) {
+ return;
+ }
float zoom = zoom_widget->get_zoom();
// Compute the minimum sizes.
@@ -153,6 +162,9 @@ void TileAtlasView::_center_view() {
}
void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
+ if (tile_set_atlas_source.is_null()) {
+ return;
+ }
base_tiles_root_control->set_tooltip_text("");
Ref<InputEventMouseMotion> mm = p_event;
@@ -169,6 +181,9 @@ void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_
}
void TileAtlasView::_draw_base_tiles() {
+ if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
+ return;
+ }
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
@@ -250,7 +265,7 @@ void TileAtlasView::_draw_base_tiles() {
Vector2 offset_pos = Rect2(base_frame_rect).get_center() + Vector2(tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin());
// Draw the tile.
- TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame);
+ TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame);
}
}
@@ -314,6 +329,9 @@ void TileAtlasView::_clear_material_canvas_items() {
}
void TileAtlasView::_draw_base_tiles_texture_grid() {
+ if (tile_set_atlas_source.is_null()) {
+ return;
+ }
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
@@ -344,6 +362,9 @@ void TileAtlasView::_draw_base_tiles_texture_grid() {
}
void TileAtlasView::_draw_base_tiles_shape_grid() {
+ if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
+ return;
+ }
// Draw the shapes.
Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
Vector2i tile_shape_size = tile_set->get_tile_size();
@@ -382,6 +403,9 @@ void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEve
}
void TileAtlasView::_draw_alternatives() {
+ if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
+ return;
+ }
// Draw the alternative tiles.
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
@@ -411,7 +435,7 @@ void TileAtlasView::_draw_alternatives() {
}
// Draw the tile.
- TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id);
+ TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id);
// Increment the x position.
current_pos.x += transposed ? texture_region_size.y : texture_region_size.x;
@@ -432,12 +456,12 @@ void TileAtlasView::_draw_background_right() {
}
void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
- tile_set = p_tile_set;
- tile_set_atlas_source = p_tile_set_atlas_source;
+ tile_set = Ref<TileSet>(p_tile_set);
+ tile_set_atlas_source = Ref<TileSetAtlasSource>(p_tile_set_atlas_source);
_clear_material_canvas_items();
- if (!tile_set) {
+ if (tile_set.is_null()) {
return;
}
@@ -485,6 +509,10 @@ void TileAtlasView::set_padding(Side p_side, int p_padding) {
}
Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp) const {
+ if (tile_set_atlas_source.is_null()) {
+ return Vector2i();
+ }
+
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (!texture.is_valid()) {
return TileSetSource::INVALID_ATLAS_COORDS;
@@ -508,6 +536,10 @@ Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p
}
void TileAtlasView::_update_alternative_tiles_rect_cache() {
+ if (tile_set_atlas_source.is_null()) {
+ return;
+ }
+
alternative_tiles_rect_cache.clear();
Rect2i current;
diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h
index e5b4863b05..8fcf942056 100644
--- a/editor/plugins/tiles/tile_atlas_view.h
+++ b/editor/plugins/tiles/tile_atlas_view.h
@@ -45,8 +45,8 @@ class TileAtlasView : public Control {
GDCLASS(TileAtlasView, Control);
private:
- TileSet *tile_set = nullptr;
- TileSetAtlasSource *tile_set_atlas_source = nullptr;
+ Ref<TileSet> tile_set;
+ Ref<TileSetAtlasSource> tile_set_atlas_source;
int source_id = TileSet::INVALID_SOURCE;
enum DragType {
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 2d0e5214db..a94080e253 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -144,7 +144,8 @@ void GenericTilePolygonEditor::_base_control_draw() {
}
// Draw tile-related things.
- Size2 tile_size = tile_set->get_tile_size();
+ const Size2 base_tile_size = tile_set->get_tile_size();
+ const Size2 tile_size = background_region.size;
Transform2D xform;
xform.set_origin(base_control->get_size() / 2 + panning);
@@ -170,12 +171,16 @@ void GenericTilePolygonEditor::_base_control_draw() {
// Draw grid.
if (current_snap_option == SNAP_GRID) {
- Vector2 spacing = tile_size / snap_subdivision->get_value();
+ Vector2 spacing = base_tile_size / snap_subdivision->get_value();
Vector2 offset = -tile_size / 2;
+ int w = snap_subdivision->get_value() * (tile_size / base_tile_size).x;
+ int h = snap_subdivision->get_value() * (tile_size / base_tile_size).y;
- for (int i = 1; i < snap_subdivision->get_value(); i++) {
- base_control->draw_line(Vector2(spacing.x * i, 0) + offset, Vector2(spacing.x * i, tile_size.y) + offset, Color(1, 1, 1, 0.33));
- base_control->draw_line(Vector2(0, spacing.y * i) + offset, Vector2(tile_size.x, spacing.y * i) + offset, Color(1, 1, 1, 0.33));
+ for (int y = 1; y < h; y++) {
+ for (int x = 1; x < w; x++) {
+ base_control->draw_line(Vector2(spacing.x * x, 0) + offset, Vector2(spacing.x * x, tile_size.y) + offset, Color(1, 1, 1, 0.33));
+ base_control->draw_line(Vector2(0, spacing.y * y) + offset, Vector2(tile_size.x, spacing.y * y) + offset, Color(1, 1, 1, 0.33));
+ }
}
}
@@ -1320,7 +1325,7 @@ TileDataDefaultEditor::TileDataDefaultEditor() {
picker_button = memnew(Button);
picker_button->set_theme_type_variation("FlatButton");
picker_button->set_toggle_mode(true);
- picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P));
+ picker_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/picker"));
toolbar->add_child(picker_button);
}
@@ -2807,7 +2812,7 @@ TileDataTerrainsEditor::TileDataTerrainsEditor() {
picker_button = memnew(Button);
picker_button->set_theme_type_variation("FlatButton");
picker_button->set_toggle_mode(true);
- picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P));
+ picker_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/picker"));
toolbar->add_child(picker_button);
// Setup
diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp
index 98c05e9b15..5fd4fb3602 100644
--- a/editor/plugins/tiles/tile_map_layer_editor.cpp
+++ b/editor/plugins/tiles/tile_map_layer_editor.cpp
@@ -2183,13 +2183,6 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
->get_viewport_control()
->connect(SceneStringName(mouse_exited), callable_mp(this, &TileMapLayerEditorTilesPlugin::_mouse_exited_viewport));
- // --- Shortcuts ---
- ED_SHORTCUT("tiles_editor/cut", TTR("Cut"), KeyModifierMask::CMD_OR_CTRL | Key::X);
- ED_SHORTCUT("tiles_editor/copy", TTR("Copy"), KeyModifierMask::CMD_OR_CTRL | Key::C);
- ED_SHORTCUT("tiles_editor/paste", TTR("Paste"), KeyModifierMask::CMD_OR_CTRL | Key::V);
- ED_SHORTCUT("tiles_editor/cancel", TTR("Cancel"), Key::ESCAPE);
- ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE);
-
// --- Initialize references ---
tile_map_clipboard.instantiate();
selection_pattern.instantiate();
@@ -2217,7 +2210,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
paint_tool_button->set_theme_type_variation("FlatButton");
paint_tool_button->set_toggle_mode(true);
paint_tool_button->set_button_group(tool_buttons_group);
- paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D));
+ paint_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/paint_tool"));
paint_tool_button->set_tooltip_text(TTR("Shift: Draw line.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Shift: Draw rectangle."));
paint_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(paint_tool_button);
@@ -2228,7 +2221,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
line_tool_button->set_toggle_mode(true);
line_tool_button->set_button_group(tool_buttons_group);
// TRANSLATORS: This refers to the line tool in the tilemap editor.
- line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", TTR("Line", "Tool"), Key::L));
+ line_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/line_tool"));
line_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(line_tool_button);
viewport_shortcut_buttons.push_back(line_tool_button);
@@ -2237,7 +2230,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
rect_tool_button->set_theme_type_variation("FlatButton");
rect_tool_button->set_toggle_mode(true);
rect_tool_button->set_button_group(tool_buttons_group);
- rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R));
+ rect_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/rect_tool"));
rect_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(rect_tool_button);
viewport_shortcut_buttons.push_back(rect_tool_button);
@@ -2246,7 +2239,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
bucket_tool_button->set_theme_type_variation("FlatButton");
bucket_tool_button->set_toggle_mode(true);
bucket_tool_button->set_button_group(tool_buttons_group);
- bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B));
+ bucket_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/bucket_tool"));
bucket_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
toolbar->add_child(tilemap_tiles_tools_buttons);
@@ -2263,7 +2256,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
picker_button = memnew(Button);
picker_button->set_theme_type_variation("FlatButton");
picker_button->set_toggle_mode(true);
- picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P));
+ picker_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/picker"));
Key key = (OS::get_singleton()->has_feature("macos") || OS::get_singleton()->has_feature("web_macos") || OS::get_singleton()->has_feature("web_ios")) ? Key::META : Key::CTRL;
picker_button->set_tooltip_text(vformat(TTR("Alternatively hold %s with other tools to pick tile."), find_keycode_name(key)));
picker_button->connect(SceneStringName(pressed), callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
@@ -2274,7 +2267,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
erase_button = memnew(Button);
erase_button->set_theme_type_variation("FlatButton");
erase_button->set_toggle_mode(true);
- erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E));
+ erase_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/eraser"));
erase_button->set_tooltip_text(TTR("Alternatively use RMB to erase tiles."));
erase_button->connect(SceneStringName(pressed), callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(erase_button);
@@ -3552,7 +3545,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() {
paint_tool_button->set_toggle_mode(true);
paint_tool_button->set_button_group(tool_buttons_group);
paint_tool_button->set_pressed(true);
- paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D));
+ paint_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/paint_tool"));
paint_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(paint_tool_button);
viewport_shortcut_buttons.push_back(paint_tool_button);
@@ -3561,7 +3554,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() {
line_tool_button->set_theme_type_variation("FlatButton");
line_tool_button->set_toggle_mode(true);
line_tool_button->set_button_group(tool_buttons_group);
- line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", TTR("Line"), Key::L));
+ line_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/line_tool"));
line_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(line_tool_button);
viewport_shortcut_buttons.push_back(line_tool_button);
@@ -3570,7 +3563,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() {
rect_tool_button->set_theme_type_variation("FlatButton");
rect_tool_button->set_toggle_mode(true);
rect_tool_button->set_button_group(tool_buttons_group);
- rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R));
+ rect_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/rect_tool"));
rect_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(rect_tool_button);
viewport_shortcut_buttons.push_back(rect_tool_button);
@@ -3579,7 +3572,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() {
bucket_tool_button->set_theme_type_variation("FlatButton");
bucket_tool_button->set_toggle_mode(true);
bucket_tool_button->set_button_group(tool_buttons_group);
- bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B));
+ bucket_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/bucket_tool"));
bucket_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
viewport_shortcut_buttons.push_back(bucket_tool_button);
@@ -3597,7 +3590,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() {
picker_button = memnew(Button);
picker_button->set_theme_type_variation("FlatButton");
picker_button->set_toggle_mode(true);
- picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P));
+ picker_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/picker"));
picker_button->connect(SceneStringName(pressed), callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(picker_button);
viewport_shortcut_buttons.push_back(picker_button);
@@ -3606,7 +3599,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() {
erase_button = memnew(Button);
erase_button->set_theme_type_variation("FlatButton");
erase_button->set_toggle_mode(true);
- erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E));
+ erase_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/eraser"));
erase_button->connect(SceneStringName(pressed), callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(erase_button);
viewport_shortcut_buttons.push_back(erase_button);
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index e4fd32a637..03070bc6b5 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2627,7 +2627,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tools_settings_erase_button = memnew(Button);
tools_settings_erase_button->set_theme_type_variation("FlatButton");
tools_settings_erase_button->set_toggle_mode(true);
- tools_settings_erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E));
+ tools_settings_erase_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/eraser"));
tools_settings_erase_button->set_shortcut_context(this);
tool_settings->add_child(tools_settings_erase_button);
@@ -2687,7 +2687,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_create_help->set_grow_direction_preset(Control::PRESET_BOTTOM_LEFT);
base_tile_popup_menu = memnew(PopupMenu);
- base_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE), TILE_DELETE);
+ base_tile_popup_menu->add_shortcut(ED_GET_SHORTCUT("tiles_editor/delete"), TILE_DELETE);
base_tile_popup_menu->add_item(TTR("Create an Alternative Tile"), TILE_CREATE_ALTERNATIVE);
base_tile_popup_menu->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
tile_atlas_view->add_child(base_tile_popup_menu);
@@ -2938,6 +2938,18 @@ bool EditorInspectorPluginTileData::parse_property(Object *p_object, const Varia
add_property_editor(p_path, ep);
return true;
}
+ } else if (p_path.begins_with("custom_data_") && p_path.trim_prefix("custom_data_").is_valid_int()) {
+ // Custom data layers.
+ int layer_index = components[0].trim_prefix("custom_data_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ EditorProperty *ep = EditorInspectorDefaultPlugin::get_editor_for_property(p_object, p_type, p_path, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
+ const TileSetAtlasSourceEditor::AtlasTileProxyObject *proxy_obj = Object::cast_to<TileSetAtlasSourceEditor::AtlasTileProxyObject>(p_object);
+ const TileSetAtlasSource *atlas_source = *proxy_obj->get_edited_tile_set_atlas_source();
+ ERR_FAIL_NULL_V(atlas_source, false);
+ const TileSet *tile_set = atlas_source->get_tile_set();
+ ERR_FAIL_NULL_V(tile_set, false);
+ add_property_editor(p_path, ep, false, tile_set->get_custom_data_layer_name(layer_index));
+ return true;
}
return false;
}
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index 6833a36138..f1cc69ce18 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -303,6 +303,19 @@ TilesEditorUtils::TilesEditorUtils() {
singleton = this;
// Pattern preview generation thread.
pattern_preview_thread.start(_thread_func, this);
+
+ ED_SHORTCUT("tiles_editor/cut", TTR("Cut"), KeyModifierMask::CMD_OR_CTRL | Key::X);
+ ED_SHORTCUT("tiles_editor/copy", TTR("Copy"), KeyModifierMask::CMD_OR_CTRL | Key::C);
+ ED_SHORTCUT("tiles_editor/paste", TTR("Paste"), KeyModifierMask::CMD_OR_CTRL | Key::V);
+ ED_SHORTCUT("tiles_editor/cancel", TTR("Cancel"), Key::ESCAPE);
+ ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE);
+
+ ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D);
+ ED_SHORTCUT("tiles_editor/line_tool", TTR("Line", "Tool"), Key::L);
+ ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R);
+ ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B);
+ ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E);
+ ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P);
}
TilesEditorUtils::~TilesEditorUtils() {
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 01551a52c2..438d798120 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -60,7 +60,6 @@
#include "scene/gui/view_panner.h"
#include "scene/main/window.h"
#include "scene/resources/curve_texture.h"
-#include "scene/resources/image_texture.h"
#include "scene/resources/style_box_flat.h"
#include "scene/resources/visual_shader_nodes.h"
#include "scene/resources/visual_shader_particle_nodes.h"
@@ -5135,7 +5134,7 @@ void VisualShaderEditor::_dup_paste_nodes(int p_type, List<CopyItem> &r_items, c
}
int new_node_id = connection_remap[item.id];
- int new_frame_id = connection_remap[node->get_frame()];
+ int new_frame_id = node->get_frame();
undo_redo->add_do_method(visual_shader.ptr(), "attach_node_to_frame", type, new_node_id, new_frame_id);
undo_redo->add_do_method(graph_plugin.ptr(), "attach_node_to_frame", type, new_node_id, new_frame_id);
}
diff --git a/editor/project_manager/project_list.cpp b/editor/project_manager/project_list.cpp
index 32f088ec60..f474464f6c 100644
--- a/editor/project_manager/project_list.cpp
+++ b/editor/project_manager/project_list.cpp
@@ -32,6 +32,8 @@
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
+#include "core/os/time.h"
+#include "core/version.h"
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
@@ -130,12 +132,29 @@ void ProjectListItemControl::set_project_icon(const Ref<Texture2D> &p_icon) {
project_icon->set_texture(p_icon);
}
+void ProjectListItemControl::set_last_edited_info(const String &p_info) {
+ last_edited_info->set_text(p_info);
+}
+
+void ProjectListItemControl::set_project_version(const String &p_info) {
+ project_version->set_text(p_info);
+}
+
void ProjectListItemControl::set_unsupported_features(PackedStringArray p_features) {
if (p_features.size() > 0) {
String tooltip_text = "";
for (int i = 0; i < p_features.size(); i++) {
if (ProjectList::project_feature_looks_like_version(p_features[i])) {
- tooltip_text += TTR("This project was last edited in a different Godot version: ") + p_features[i] + "\n";
+ PackedStringArray project_version_split = p_features[i].split(".");
+ int project_version_major = 0, project_version_minor = 0;
+ if (project_version_split.size() >= 2) {
+ project_version_major = project_version_split[0].to_int();
+ project_version_minor = project_version_split[1].to_int();
+ }
+ if (VERSION_MAJOR != project_version_major || VERSION_MINOR <= project_version_minor) {
+ // Don't show a warning if the project was last edited in a previous minor version.
+ tooltip_text += TTR("This project was last edited in a different Godot version: ") + p_features[i] + "\n";
+ }
p_features.remove_at(i);
i--;
}
@@ -144,6 +163,10 @@ void ProjectListItemControl::set_unsupported_features(PackedStringArray p_featur
String unsupported_features_str = String(", ").join(p_features);
tooltip_text += TTR("This project uses features unsupported by the current build:") + "\n" + unsupported_features_str;
}
+ if (tooltip_text.is_empty()) {
+ return;
+ }
+ project_version->set_tooltip_text(tooltip_text);
project_unsupported_features->set_tooltip_text(tooltip_text);
project_unsupported_features->show();
} else {
@@ -272,12 +295,24 @@ ProjectListItemControl::ProjectListItemControl() {
project_path->set_modulate(Color(1, 1, 1, 0.5));
path_hb->add_child(project_path);
+ last_edited_info = memnew(Label);
+ last_edited_info->set_name("LastEditedInfo");
+ last_edited_info->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ last_edited_info->set_tooltip_text("Last edited timestamp");
+ last_edited_info->set_modulate(Color(1, 1, 1, 0.5));
+ path_hb->add_child(last_edited_info);
+
project_unsupported_features = memnew(TextureRect);
project_unsupported_features->set_name("ProjectUnsupportedFeatures");
project_unsupported_features->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
path_hb->add_child(project_unsupported_features);
project_unsupported_features->hide();
+ project_version = memnew(Label);
+ project_version->set_name("ProjectVersion");
+ project_version->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ path_hb->add_child(project_version);
+
Control *spacer = memnew(Control);
spacer->set_custom_minimum_size(Size2(10, 10));
path_hb->add_child(spacer);
@@ -409,6 +444,24 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa
PackedStringArray project_features = cf->get_value("application", "config/features", PackedStringArray());
PackedStringArray unsupported_features = ProjectSettings::get_unsupported_features(project_features);
+ String project_version = "?";
+ for (int i = 0; i < project_features.size(); i++) {
+ if (ProjectList::project_feature_looks_like_version(project_features[i])) {
+ project_version = project_features[i];
+ break;
+ }
+ }
+
+ if (config_version < ProjectSettings::CONFIG_VERSION) {
+ // Previous versions may not have unsupported features.
+ if (config_version == 4) {
+ unsupported_features.push_back("3.x");
+ project_version = "3.x";
+ } else {
+ unsupported_features.push_back("Unknown version");
+ }
+ }
+
uint64_t last_edited = 0;
if (cf_err == OK) {
// The modification date marks the date the project was last edited.
@@ -433,7 +486,7 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa
ProjectManager::get_singleton()->add_new_tag(tag);
}
- return Item(project_name, description, tags, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version);
+ return Item(project_name, description, project_version, tags, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version);
}
void ProjectList::_update_icons_async() {
@@ -706,6 +759,8 @@ void ProjectList::_create_project_item_control(int p_index) {
hb->set_tooltip_text(item.description);
hb->set_tags(item.tags, this);
hb->set_unsupported_features(item.unsupported_features.duplicate());
+ hb->set_project_version(item.project_version);
+ hb->set_last_edited_info(Time::get_singleton()->get_datetime_string_from_unix_time(item.last_edited, true));
hb->set_is_favorite(item.favorite);
hb->set_is_missing(item.missing);
diff --git a/editor/project_manager/project_list.h b/editor/project_manager/project_list.h
index 981df0f3a0..6e0f5830ac 100644
--- a/editor/project_manager/project_list.h
+++ b/editor/project_manager/project_list.h
@@ -51,6 +51,8 @@ class ProjectListItemControl : public HBoxContainer {
TextureRect *project_icon = nullptr;
Label *project_title = nullptr;
Label *project_path = nullptr;
+ Label *last_edited_info = nullptr;
+ Label *project_version = nullptr;
TextureRect *project_unsupported_features = nullptr;
HBoxContainer *tag_container = nullptr;
@@ -71,6 +73,8 @@ public:
void set_project_path(const String &p_path);
void set_tags(const PackedStringArray &p_tags, ProjectList *p_parent_list);
void set_project_icon(const Ref<Texture2D> &p_icon);
+ void set_last_edited_info(const String &p_info);
+ void set_project_version(const String &p_version);
void set_unsupported_features(PackedStringArray p_features);
bool should_load_project_icon() const;
@@ -100,6 +104,7 @@ public:
struct Item {
String project_name;
String description;
+ String project_version;
PackedStringArray tags;
String tag_sort_string;
String path;
@@ -118,6 +123,7 @@ public:
Item(const String &p_name,
const String &p_description,
+ const String &p_project_version,
const PackedStringArray &p_tags,
const String &p_path,
const String &p_icon,
@@ -130,6 +136,7 @@ public:
int p_version) {
project_name = p_name;
description = p_description;
+ project_version = p_project_version;
tags = p_tags;
path = p_path;
icon = p_icon;
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index cff95cadc8..dc9d1df4e8 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -54,7 +54,7 @@ void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) {
if (saved_size != Rect2()) {
popup(saved_size);
} else {
- popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8);
+ popup_centered_clamped(Size2(1200, 700) * EDSCALE, 0.8);
}
_add_feature_overrides();
diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp
index 51f9d319a6..edefaa61a8 100644
--- a/editor/rename_dialog.cpp
+++ b/editor/rename_dialog.cpp
@@ -289,6 +289,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor) {
vbc->add_child(lbl_preview_title);
lbl_preview = memnew(Label);
+ lbl_preview->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
lbl_preview->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
vbc->add_child(lbl_preview);
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 53145c3450..9209c26876 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -56,6 +56,7 @@
#include "editor/shader_create_dialog.h"
#include "editor/themes/editor_scale.h"
#include "scene/animation/animation_tree.h"
+#include "scene/audio/audio_stream_player.h"
#include "scene/gui/check_box.h"
#include "scene/property_utils.h"
#include "scene/resources/packed_scene.h"
@@ -254,8 +255,8 @@ void SceneTreeDock::instantiate_scenes(const Vector<String> &p_files, Node *p_pa
_perform_instantiate_scenes(p_files, parent, -1);
}
-void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, Node *parent, int p_pos) {
- ERR_FAIL_NULL(parent);
+void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, Node *p_parent, int p_pos) {
+ ERR_FAIL_NULL(p_parent);
Vector<Node *> instances;
@@ -302,25 +303,25 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Instantiate Scene(s)"));
+ undo_redo->create_action_for_history(TTRN("Instantiate Scene", "Instantiate Scenes", instances.size()), editor_data->get_current_edited_scene_history_id());
+ undo_redo->add_do_method(editor_selection, "clear");
for (int i = 0; i < instances.size(); i++) {
Node *instantiated_scene = instances[i];
- undo_redo->add_do_method(parent, "add_child", instantiated_scene, true);
+ undo_redo->add_do_method(p_parent, "add_child", instantiated_scene, true);
if (p_pos >= 0) {
- undo_redo->add_do_method(parent, "move_child", instantiated_scene, p_pos + i);
+ undo_redo->add_do_method(p_parent, "move_child", instantiated_scene, p_pos + i);
}
undo_redo->add_do_method(instantiated_scene, "set_owner", edited_scene);
- undo_redo->add_do_method(editor_selection, "clear");
undo_redo->add_do_method(editor_selection, "add_node", instantiated_scene);
undo_redo->add_do_reference(instantiated_scene);
- undo_redo->add_undo_method(parent, "remove_child", instantiated_scene);
+ undo_redo->add_undo_method(p_parent, "remove_child", instantiated_scene);
- String new_name = parent->validate_child_name(instantiated_scene);
+ String new_name = p_parent->validate_child_name(instantiated_scene);
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
- undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(parent), p_files[i], new_name);
- undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).path_join(new_name)));
+ undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(p_parent), p_files[i], new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).path_join(new_name)));
}
undo_redo->commit_action();
@@ -330,6 +331,75 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N
}
}
+void SceneTreeDock::_perform_create_audio_stream_players(const Vector<String> &p_files, Node *p_parent, int p_pos) {
+ ERR_FAIL_NULL(p_parent);
+
+ StringName node_type = "AudioStreamPlayer";
+ if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
+ if (Object::cast_to<Node2D>(p_parent)) {
+ node_type = "AudioStreamPlayer2D";
+ } else if (Object::cast_to<Node3D>(p_parent)) {
+ node_type = "AudioStreamPlayer3D";
+ }
+ }
+
+ Vector<Node *> nodes;
+ bool error = false;
+
+ for (const String &path : p_files) {
+ Ref<AudioStream> stream = ResourceLoader::load(path);
+ if (stream.is_null()) {
+ current_option = -1;
+ accept->set_text(vformat(TTR("Error loading audio stream from %s"), path));
+ accept->popup_centered();
+ error = true;
+ break;
+ }
+
+ Node *player = Object::cast_to<Node>(ClassDB::instantiate(node_type));
+ player->set("stream", stream);
+
+ // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
+ const String &node_name = Node::adjust_name_casing(path.get_file().get_basename());
+ if (!node_name.is_empty()) {
+ player->set_name(node_name);
+ }
+
+ nodes.push_back(player);
+ }
+
+ if (error) {
+ for (Node *node : nodes) {
+ memdelete(node);
+ }
+ return;
+ }
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action_for_history(TTRN("Create AudioStreamPlayer", "Create AudioStreamPlayers", nodes.size()), editor_data->get_current_edited_scene_history_id());
+ undo_redo->add_do_method(editor_selection, "clear");
+
+ for (int i = 0; i < nodes.size(); i++) {
+ Node *node = nodes[i];
+
+ undo_redo->add_do_method(p_parent, "add_child", node, true);
+ if (p_pos >= 0) {
+ undo_redo->add_do_method(p_parent, "move_child", node, p_pos + i);
+ }
+ undo_redo->add_do_method(node, "set_owner", edited_scene);
+ undo_redo->add_do_method(editor_selection, "add_node", node);
+ undo_redo->add_do_reference(node);
+ undo_redo->add_undo_method(p_parent, "remove_child", node);
+
+ String new_name = p_parent->validate_child_name(node);
+ EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
+ undo_redo->add_do_method(ed, "live_debug_create_node", edited_scene->get_path_to(p_parent), node->get_class(), new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).path_join(new_name)));
+ }
+
+ undo_redo->commit_action();
+}
+
void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) {
// `move_child` + `get_index` doesn't really work for internal nodes.
ERR_FAIL_COND_MSG(base->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to replace internal node, this is not supported.");
@@ -3034,7 +3104,23 @@ void SceneTreeDock::set_edited_scene(Node *p_scene) {
edited_scene = p_scene;
}
+static bool _is_same_selection(const Vector<Node *> &p_first, const List<Node *> &p_second) {
+ if (p_first.size() != p_second.size()) {
+ return false;
+ }
+ for (Node *node : p_second) {
+ if (!p_first.has(node)) {
+ return false;
+ }
+ }
+ return true;
+}
+
void SceneTreeDock::set_selection(const Vector<Node *> &p_nodes) {
+ // If the nodes selected are the same independently of order then return early.
+ if (_is_same_selection(p_nodes, editor_selection->get_full_selected_node_list())) {
+ return;
+ }
editor_selection->clear();
for (Node *node : p_nodes) {
editor_selection->add_node(node);
@@ -3196,15 +3282,13 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) {
void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to, int p_type) {
Node *node = get_node(p_to);
ERR_FAIL_NULL(node);
+ ERR_FAIL_COND(p_files.is_empty());
- if (scene_tree->get_scene_tree()->get_drop_mode_flags() & Tree::DROP_MODE_INBETWEEN) {
- // Dropped PackedScene, instance it.
- int to_pos = -1;
- _normalize_drop(node, to_pos, p_type);
- _perform_instantiate_scenes(p_files, node, to_pos);
- } else {
- const String &res_path = p_files[0];
- StringName res_type = EditorFileSystem::get_singleton()->get_file_type(res_path);
+ const String &res_path = p_files[0];
+ const StringName res_type = EditorFileSystem::get_singleton()->get_file_type(res_path);
+
+ // Dropping as property when possible.
+ if (p_type == 0 && p_files.size() == 1) {
List<String> valid_properties;
List<PropertyInfo> pinfo;
@@ -3238,10 +3322,22 @@ void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to,
menu_properties->reset_size();
menu_properties->set_position(get_screen_position() + get_local_mouse_position());
menu_properties->popup();
- } else if (!valid_properties.is_empty()) {
+ return;
+ }
+ if (!valid_properties.is_empty()) {
_perform_property_drop(node, valid_properties.front()->get(), ResourceLoader::load(res_path));
+ return;
}
}
+
+ // Either instantiate scenes or create AudioStreamPlayers.
+ int to_pos = -1;
+ _normalize_drop(node, to_pos, p_type);
+ if (ClassDB::is_parent_class(res_type, "PackedScene")) {
+ _perform_instantiate_scenes(p_files, node, to_pos);
+ } else if (ClassDB::is_parent_class(res_type, "AudioStream")) {
+ _perform_create_audio_stream_players(p_files, node, to_pos);
+ }
}
void SceneTreeDock::_script_dropped(const String &p_file, NodePath p_to) {
@@ -3571,7 +3667,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
if (profile_allow_editing) {
menu->add_separator();
- menu->add_icon_shortcut(get_editor_theme_icon(SNAME("Remove")), ED_SHORTCUT("scene_tree/delete", TTR("Delete Node(s)"), Key::KEY_DELETE), TOOL_ERASE);
+ menu->add_icon_shortcut(get_editor_theme_icon(SNAME("Remove")), ED_GET_SHORTCUT("scene_tree/delete"), TOOL_ERASE);
}
menu->reset_size();
menu->set_position(p_menu_pos);
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 21e1b00f93..abef990995 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -272,7 +272,8 @@ class SceneTreeDock : public VBoxContainer {
void _filter_option_selected(int option);
void _append_filter_options_to(PopupMenu *p_menu, bool p_include_separator = true);
- void _perform_instantiate_scenes(const Vector<String> &p_files, Node *parent, int p_pos);
+ void _perform_instantiate_scenes(const Vector<String> &p_files, Node *p_parent, int p_pos);
+ void _perform_create_audio_stream_players(const Vector<String> &p_files, Node *p_parent, int p_pos);
void _replace_with_branch_scene(const String &p_file, Node *base);
void _remote_tree_selected();
diff --git a/editor/themes/SCsub b/editor/themes/SCsub
index 65cfb6a8be..e8f96e4299 100644
--- a/editor/themes/SCsub
+++ b/editor/themes/SCsub
@@ -3,8 +3,8 @@
Import("env")
import glob
-import editor_theme_builders
+import editor_theme_builders
# Fonts
flist = glob.glob(env.Dir("#thirdparty").abspath + "/fonts/*.ttf")