diff options
27 files changed, 256 insertions, 225 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 562bde978e..9fe54e57a7 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -348,7 +348,6 @@ bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const { _THREAD_SAFE_METHOD_ if (!props.has(p_name)) { - WARN_PRINT("Property not found: " + String(p_name)); return false; } r_ret = props[p_name].variant; diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 3bfa022382..109999d612 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -845,29 +845,27 @@ Error ResourceLoaderBinary::load() { } } - if (ClassDB::has_property(res->get_class_name(), name)) { - if (value.get_type() == Variant::ARRAY) { - Array set_array = value; - bool is_get_valid = false; - Variant get_value = res->get(name, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::ARRAY) { - Array get_array = get_value; - if (!set_array.is_same_typed(get_array)) { - value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); - } + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = res->get(name, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); } } + } - if (value.get_type() == Variant::DICTIONARY) { - Dictionary set_dict = value; - bool is_get_valid = false; - Variant get_value = res->get(name, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { - Dictionary get_dict = get_value; - if (!set_dict.is_same_typed(get_dict)) { - value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), - get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); - } + if (value.get_type() == Variant::DICTIONARY) { + Dictionary set_dict = value; + bool is_get_valid = false; + Variant get_value = res->get(name, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { + Dictionary get_dict = get_value; + if (!set_dict.is_same_typed(get_dict)) { + value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), + get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); } } } diff --git a/doc/classes/EditorContextMenuPlugin.xml b/doc/classes/EditorContextMenuPlugin.xml index 71c4ca0f9b..fb90a2a5cd 100644 --- a/doc/classes/EditorContextMenuPlugin.xml +++ b/doc/classes/EditorContextMenuPlugin.xml @@ -47,6 +47,24 @@ [/codeblock] </description> </method> + <method name="add_context_submenu_item"> + <return type="void" /> + <param index="0" name="name" type="String" /> + <param index="1" name="menu" type="PopupMenu" /> + <param index="2" name="icon" type="Texture2D" default="null" /> + <description> + Add a submenu to the context menu of the plugin's specified slot. The submenu is not automatically handled, you need to connect to its signals yourself. Also the submenu is freed on every popup, so provide a new [PopupMenu] every time. + [codeblock] + func _popup_menu(paths): + var popup_menu = PopupMenu.new() + popup_menu.add_item("Blue") + popup_menu.add_item("White") + popup_menu.id_pressed.connect(_on_color_submenu_option) + + add_context_menu_item("Set Node Color", popup_menu) + [/codeblock] + </description> + </method> <method name="add_menu_shortcut"> <return type="void" /> <param index="0" name="shortcut" type="Shortcut" /> diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 2853ebc499..d88fb134f1 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1983,7 +1983,7 @@ void EditorNode::try_autosave() { editor_data.save_editor_external_data(); } -void EditorNode::restart_editor() { +void EditorNode::restart_editor(bool p_goto_project_manager) { exiting = true; if (project_run_bar->is_playing()) { @@ -1991,22 +1991,25 @@ void EditorNode::restart_editor() { } String to_reopen; - if (get_tree()->get_edited_scene_root()) { + if (!p_goto_project_manager && get_tree()->get_edited_scene_root()) { to_reopen = get_tree()->get_edited_scene_root()->get_scene_file_path(); } _exit_editor(EXIT_SUCCESS); List<String> args; - for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_TOOL)) { args.push_back(a); } - args.push_back("--path"); - args.push_back(ProjectSettings::get_singleton()->get_resource_path()); + if (p_goto_project_manager) { + args.push_back("--project-manager"); + } else { + args.push_back("--path"); + args.push_back(ProjectSettings::get_singleton()->get_resource_path()); - args.push_back("-e"); + args.push_back("-e"); + } if (!to_reopen.is_empty()) { args.push_back(to_reopen); @@ -2387,7 +2390,7 @@ void EditorNode::hide_unused_editors(const Object *p_editing_owner) { // This is to sweep properties that were removed from the inspector. List<ObjectID> to_remove; for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) { - const Object *context = ObjectDB::get_instance(kv.key); + Object *context = ObjectDB::get_instance(kv.key); if (context) { // In case of self-owning plugins, they are disabled here if they can auto hide. const EditorPlugin *self_owning = Object::cast_to<EditorPlugin>(context); @@ -2396,7 +2399,7 @@ void EditorNode::hide_unused_editors(const Object *p_editing_owner) { } } - if (!context) { + if (!context || context->call(SNAME("_should_stop_editing"))) { to_remove.push_back(kv.key); for (EditorPlugin *plugin : kv.value) { if (plugin->can_auto_hide()) { @@ -3402,23 +3405,7 @@ void EditorNode::_discard_changes(const String &p_str) { } break; case RUN_PROJECT_MANAGER: { - project_run_bar->stop_playing(); - _exit_editor(EXIT_SUCCESS); - String exec = OS::get_singleton()->get_executable_path(); - - List<String> args; - for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_TOOL)) { - args.push_back(a); - } - - String exec_base_dir = exec.get_base_dir(); - if (!exec_base_dir.is_empty()) { - args.push_back("--path"); - args.push_back(exec_base_dir); - } - args.push_back("--project-manager"); - - OS::get_singleton()->set_restart_on_exit(true, args); + restart_editor(true); } break; case RELOAD_CURRENT_PROJECT: { restart_editor(); diff --git a/editor/editor_node.h b/editor/editor_node.h index 55caed4bb4..696caf857c 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -926,7 +926,7 @@ public: void save_scene_list(const HashSet<String> &p_scene_paths); void save_before_run(); void try_autosave(); - void restart_editor(); + void restart_editor(bool p_goto_project_manager = false); void unload_editor_addons(); void dim_editor(bool p_dimming); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 897e7835fd..c5a35e466c 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -3179,6 +3179,10 @@ void EditorPropertyResource::_update_preferred_shader() { } } +bool EditorPropertyResource::_should_stop_editing() const { + return !resource_picker->is_toggle_pressed(); +} + void EditorPropertyResource::_viewport_selected(const NodePath &p_path) { Node *to_node = get_node(p_path); if (!Object::cast_to<Viewport>(to_node)) { @@ -3353,13 +3357,18 @@ void EditorPropertyResource::_notification(int p_what) { switch (p_what) { case NOTIFICATION_EXIT_TREE: { const EditorInspector *ei = get_parent_inspector(); - if (ei && !ei->is_main_editor_inspector()) { + const EditorInspector *main_ei = InspectorDock::get_inspector_singleton(); + if (ei && main_ei && ei != main_ei && !main_ei->is_ancestor_of(ei)) { fold_resource(); } } break; } } +void EditorPropertyResource::_bind_methods() { + ClassDB::bind_method(D_METHOD("_should_stop_editing"), &EditorPropertyResource::_should_stop_editing); +} + EditorPropertyResource::EditorPropertyResource() { use_sub_inspector = bool(EDITOR_GET("interface/inspector/open_resources_in_current_inspector")); has_borders = true; diff --git a/editor/editor_properties.h b/editor/editor_properties.h index 2ec78cdb44..004630da3e 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -683,10 +683,12 @@ class EditorPropertyResource : public EditorProperty { void _open_editor_pressed(); void _update_preferred_shader(); + bool _should_stop_editing() const; protected: virtual void _set_read_only(bool p_read_only) override; void _notification(int p_what); + static void _bind_methods(); public: virtual void update_property() override; diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index e4ae2a6202..0f0287718c 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -132,6 +132,11 @@ void EditorResourcePicker::_resource_selected() { emit_signal(SNAME("resource_selected"), edited_resource, false); } +void EditorResourcePicker::_resource_changed() { + emit_signal(SNAME("resource_changed"), edited_resource); + _update_resource(); +} + void EditorResourcePicker::_file_selected(const String &p_path) { Ref<Resource> loaded_resource = ResourceLoader::load(p_path); ERR_FAIL_COND_MSG(loaded_resource.is_null(), "Cannot load resource from path '" + p_path + "'."); @@ -167,8 +172,7 @@ void EditorResourcePicker::_file_selected(const String &p_path) { } edited_resource = loaded_resource; - emit_signal(SNAME("resource_changed"), edited_resource); - _update_resource(); + _resource_changed(); } void EditorResourcePicker::_resource_saved(Object *p_resource) { @@ -353,8 +357,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { case OBJ_MENU_CLEAR: { edited_resource = Ref<Resource>(); - emit_signal(SNAME("resource_changed"), edited_resource); - _update_resource(); + _resource_changed(); } break; case OBJ_MENU_MAKE_UNIQUE: { @@ -366,8 +369,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { ERR_FAIL_COND(unique_resource.is_null()); // duplicate() may fail. edited_resource = unique_resource; - emit_signal(SNAME("resource_changed"), edited_resource); - _update_resource(); + _resource_changed(); } break; case OBJ_MENU_MAKE_UNIQUE_RECURSIVE: { @@ -432,9 +434,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { _edit_menu_cbk(OBJ_MENU_MAKE_UNIQUE); return; } - - emit_signal(SNAME("resource_changed"), edited_resource); - _update_resource(); + _resource_changed(); } break; case OBJ_MENU_SHOW_IN_FILE_SYSTEM: { @@ -453,8 +453,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { ERR_FAIL_INDEX(to_type, conversions.size()); edited_resource = conversions[to_type]->convert(edited_resource); - emit_signal(SNAME("resource_changed"), edited_resource); - _update_resource(); + _resource_changed(); break; } @@ -481,8 +480,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { // Prevent freeing of the object until the end of the update of the resource (GH-88286). Ref<Resource> old_edited_resource = edited_resource; edited_resource = Ref<Resource>(resp); - emit_signal(SNAME("resource_changed"), edited_resource); - _update_resource(); + _resource_changed(); } break; } } @@ -778,8 +776,7 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_ } edited_resource = dropped_resource; - emit_signal(SNAME("resource_changed"), edited_resource); - _update_resource(); + _resource_changed(); } } @@ -952,6 +949,10 @@ void EditorResourcePicker::set_toggle_pressed(bool p_pressed) { assign_button->set_pressed(p_pressed); } +bool EditorResourcePicker::is_toggle_pressed() const { + return assign_button->is_pressed(); +} + void EditorResourcePicker::set_editable(bool p_editable) { editable = p_editable; assign_button->set_disabled(!editable && !edited_resource.is_valid()); @@ -1046,8 +1047,7 @@ void EditorResourcePicker::_duplicate_selected_resources() { if (meta.size() == 1) { // Root. edited_resource = unique_resource; - emit_signal(SNAME("resource_changed"), edited_resource); - _update_resource(); + _resource_changed(); } else { Array parent_meta = item->get_parent()->get_metadata(0); Ref<Resource> parent = parent_meta[0]; diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index c39d9af764..0a32dea3ed 100644 --- a/editor/editor_resource_picker.h +++ b/editor/editor_resource_picker.h @@ -86,6 +86,7 @@ class EditorResourcePicker : public HBoxContainer { void _update_resource_preview(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, ObjectID p_obj); void _resource_selected(); + void _resource_changed(); void _file_selected(const String &p_path); void _resource_saved(Object *p_resource); @@ -134,6 +135,7 @@ public: void set_toggle_mode(bool p_enable); bool is_toggle_mode() const; void set_toggle_pressed(bool p_pressed); + bool is_toggle_pressed() const; void set_editable(bool p_editable); bool is_editable() const; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index ceaffb64c4..ee06f08a2d 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -221,7 +221,6 @@ bool EditorSettings::_get(const StringName &p_name, Variant &r_ret) const { const VariantContainer *v = props.getptr(p_name); if (!v) { - WARN_PRINT("EditorSettings::_get - Property not found: " + String(p_name)); return false; } r_ret = v->variant; diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp index a63c3f7848..77d0ba7a60 100644 --- a/editor/gui/editor_file_dialog.cpp +++ b/editor/gui/editor_file_dialog.cpp @@ -1580,7 +1580,7 @@ void EditorFileDialog::_favorite_move_down() { } void EditorFileDialog::_update_favorites() { - bool res = (access == ACCESS_RESOURCES); + bool access_resources = (access == ACCESS_RESOURCES); String current = get_current_dir(); favorites->clear(); @@ -1596,8 +1596,11 @@ void EditorFileDialog::_update_favorites() { for (int i = 0; i < favorited.size(); i++) { String name = favorited[i]; - bool cres = name.begins_with("res://"); - if (cres != res || !name.ends_with("/")) { + if (access_resources != name.begins_with("res://")) { + continue; + } + + if (!name.ends_with("/")) { continue; } @@ -1609,7 +1612,7 @@ void EditorFileDialog::_update_favorites() { } // Compute favorite display text. - if (res && name == "res://") { + if (access_resources && name == "res://") { if (name == current) { current_favorite = favorited_paths.size(); } @@ -1620,7 +1623,7 @@ void EditorFileDialog::_update_favorites() { if (name == current || name == current + "/") { current_favorite = favorited_paths.size(); } - name = name.substr(0, name.length() - 1); + name = name.trim_suffix("/"); name = name.get_file(); favorited_paths.append(favorited[i]); favorited_names.append(name); @@ -1647,7 +1650,7 @@ void EditorFileDialog::_update_favorites() { } void EditorFileDialog::_favorite_pressed() { - bool res = (access == ACCESS_RESOURCES); + bool access_resources = (access == ACCESS_RESOURCES); String cd = get_current_dir(); if (!cd.ends_with("/")) { @@ -1657,13 +1660,12 @@ void EditorFileDialog::_favorite_pressed() { Vector<String> favorited = EditorSettings::get_singleton()->get_favorites(); bool found = false; - for (int i = 0; i < favorited.size(); i++) { - bool cres = favorited[i].begins_with("res://"); - if (cres != res) { + for (const String &name : favorited) { + if (access_resources != name.begins_with("res://")) { continue; } - if (favorited[i] == cd) { + if (name == cd) { found = true; break; } @@ -1683,31 +1685,30 @@ void EditorFileDialog::_favorite_pressed() { void EditorFileDialog::_update_recent() { recent->clear(); - bool res = (access == ACCESS_RESOURCES); + bool access_resources = (access == ACCESS_RESOURCES); Vector<String> recentd = EditorSettings::get_singleton()->get_recent_dirs(); Vector<String> recentd_paths; Vector<String> recentd_names; + bool modified = false; for (int i = 0; i < recentd.size(); i++) { - bool cres = recentd[i].begins_with("res://"); - if (cres != res) { + String name = recentd[i]; + if (access_resources != name.begins_with("res://")) { continue; } - if (!dir_access->dir_exists(recentd[i])) { + if (!dir_access->dir_exists(name)) { // Remove invalid directory from the list of Recent directories. recentd.remove_at(i--); + modified = true; continue; } // Compute recent directory display text. - String name = recentd[i]; - if (res && name == "res://") { + if (access_resources && name == "res://") { name = "/"; } else { - if (name.ends_with("/")) { - name = name.substr(0, name.length() - 1); - } + name = name.trim_suffix("/"); name = name.get_file(); } recentd_paths.append(recentd[i]); @@ -1721,7 +1722,10 @@ void EditorFileDialog::_update_recent() { recent->set_item_metadata(-1, recentd_paths[i]); recent->set_item_icon_modulate(-1, get_dir_icon_color(recentd_paths[i])); } - EditorSettings::get_singleton()->set_recent_dirs(recentd); + + if (modified) { + EditorSettings::get_singleton()->set_recent_dirs(recentd); + } } void EditorFileDialog::_recent_selected(int p_idx) { diff --git a/editor/plugins/editor_context_menu_plugin.cpp b/editor/plugins/editor_context_menu_plugin.cpp index 0648327fab..b635816bd9 100644 --- a/editor/plugins/editor_context_menu_plugin.cpp +++ b/editor/plugins/editor_context_menu_plugin.cpp @@ -67,10 +67,21 @@ void EditorContextMenuPlugin::add_context_menu_item_from_shortcut(const String & context_menu_items.insert(p_name, item); } +void EditorContextMenuPlugin::add_context_submenu_item(const String &p_name, PopupMenu *p_menu, const Ref<Texture2D> &p_texture) { + ERR_FAIL_NULL(p_menu); + + ContextMenuItem item; + item.item_name = p_name; + item.icon = p_texture; + item.submenu = p_menu; + context_menu_items.insert(p_name, item); +} + void EditorContextMenuPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("add_menu_shortcut", "shortcut", "callback"), &EditorContextMenuPlugin::add_menu_shortcut); ClassDB::bind_method(D_METHOD("add_context_menu_item", "name", "callback", "icon"), &EditorContextMenuPlugin::add_context_menu_item, DEFVAL(Ref<Texture2D>())); ClassDB::bind_method(D_METHOD("add_context_menu_item_from_shortcut", "name", "shortcut", "icon"), &EditorContextMenuPlugin::add_context_menu_item_from_shortcut, DEFVAL(Ref<Texture2D>())); + ClassDB::bind_method(D_METHOD("add_context_submenu_item", "name", "menu", "icon"), &EditorContextMenuPlugin::add_context_submenu_item, DEFVAL(Ref<Texture2D>())); GDVIRTUAL_BIND(_popup_menu, "paths"); @@ -117,12 +128,17 @@ void EditorContextMenuPluginManager::add_options_from_plugins(PopupMenu *p_popup EditorContextMenuPlugin::ContextMenuItem &item = E.value; item.id = id; - if (item.icon.is_valid()) { - p_popup->add_icon_item(item.icon, item.item_name, id); - p_popup->set_item_icon_max_width(-1, icon_size); + if (item.submenu) { + p_popup->add_submenu_node_item(item.item_name, item.submenu, id); } else { p_popup->add_item(item.item_name, id); } + + if (item.icon.is_valid()) { + p_popup->set_item_icon(-1, item.icon); + p_popup->set_item_icon_max_width(-1, icon_size); + } + if (item.shortcut.is_valid()) { p_popup->set_item_shortcut(-1, item.shortcut, true); } diff --git a/editor/plugins/editor_context_menu_plugin.h b/editor/plugins/editor_context_menu_plugin.h index 0232d254ba..86c67dedda 100644 --- a/editor/plugins/editor_context_menu_plugin.h +++ b/editor/plugins/editor_context_menu_plugin.h @@ -65,6 +65,7 @@ public: Callable callable; Ref<Texture2D> icon; Ref<Shortcut> shortcut; + PopupMenu *submenu = nullptr; }; HashMap<String, ContextMenuItem> context_menu_items; HashMap<Ref<Shortcut>, Callable> context_menu_shortcuts; @@ -80,6 +81,7 @@ public: void add_menu_shortcut(const Ref<Shortcut> &p_shortcut, const Callable &p_callable); void add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref<Texture2D> &p_texture); void add_context_menu_item_from_shortcut(const String &p_name, const Ref<Shortcut> &p_shortcut, const Ref<Texture2D> &p_texture); + void add_context_submenu_item(const String &p_name, PopupMenu *p_menu, const Ref<Texture2D> &p_texture); }; VARIANT_ENUM_CAST(EditorContextMenuPlugin::ContextMenuSlot); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index fe7301975f..0cf194b7fe 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -4255,8 +4255,31 @@ Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const ray_params.to = world_pos + world_ray * camera->get_far(); PhysicsDirectSpaceState3D::RayResult result; - if (ss->intersect_ray(ray_params, result)) { - return result.position; + if (ss->intersect_ray(ray_params, result) && preview_node->get_child_count() > 0) { + // Calculate an offset for the `preview_node` such that the its bounding box is on top of and touching the contact surface's plane. + + // Use the Gram-Schmidt process to get an orthonormal Basis aligned with the surface normal. + const Vector3 bb_basis_x = result.normal; + Vector3 bb_basis_y = Vector3(0, 1, 0); + bb_basis_y = bb_basis_y - bb_basis_y.project(bb_basis_x); + if (bb_basis_y.is_zero_approx()) { + bb_basis_y = Vector3(0, 0, 1); + bb_basis_y = bb_basis_y - bb_basis_y.project(bb_basis_x); + } + bb_basis_y = bb_basis_y.normalized(); + const Vector3 bb_basis_z = bb_basis_x.cross(bb_basis_y); + const Basis bb_basis = Basis(bb_basis_x, bb_basis_y, bb_basis_z); + + // This normal-aligned Basis allows us to create an AABB that can fit on the surface plane as snugly as possible. + const Transform3D bb_transform = Transform3D(bb_basis, preview_node->get_transform().origin); + const AABB preview_node_bb = _calculate_spatial_bounds(preview_node, true, &bb_transform); + // The x-axis's alignment with the surface normal also makes it trivial to get the distance from `preview_node`'s origin at (0, 0, 0) to the correct AABB face. + const float offset_distance = -preview_node_bb.position.x; + + // `result_offset` is in global space. + const Vector3 result_offset = result.position + result.normal * offset_distance; + + return result_offset; } const bool is_orthogonal = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL; @@ -4284,18 +4307,21 @@ Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const return world_pos + world_ray * FALLBACK_DISTANCE; } -AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, const Node3D *p_top_level_parent) { +AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, bool p_omit_top_level, const Transform3D *p_bounds_orientation) { AABB bounds; - if (!p_top_level_parent) { - p_top_level_parent = p_parent; + Transform3D bounds_orientation; + if (p_bounds_orientation) { + bounds_orientation = *p_bounds_orientation; + } else { + bounds_orientation = p_parent->get_global_transform(); } if (!p_parent) { return AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4)); } - Transform3D xform_to_top_level_parent_space = p_top_level_parent->get_global_transform().affine_inverse() * p_parent->get_global_transform(); + const Transform3D xform_to_top_level_parent_space = bounds_orientation.affine_inverse() * p_parent->get_global_transform(); const VisualInstance3D *visual_instance = Object::cast_to<VisualInstance3D>(p_parent); if (visual_instance) { @@ -4306,9 +4332,9 @@ AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, con bounds = xform_to_top_level_parent_space.xform(bounds); for (int i = 0; i < p_parent->get_child_count(); i++) { - Node3D *child = Object::cast_to<Node3D>(p_parent->get_child(i)); - if (child) { - AABB child_bounds = _calculate_spatial_bounds(child, p_top_level_parent); + const Node3D *child = Object::cast_to<Node3D>(p_parent->get_child(i)); + if (child && !(p_omit_top_level && child->is_set_as_top_level())) { + const AABB child_bounds = _calculate_spatial_bounds(child, p_omit_top_level, &bounds_orientation); bounds.merge_with(child_bounds); } } @@ -4359,6 +4385,10 @@ void Node3DEditorViewport::_create_preview_node(const Vector<String> &files) con if (instance) { instance = _sanitize_preview_node(instance); preview_node->add_child(instance); + Node3D *node_3d = Object::cast_to<Node3D>(instance); + if (node_3d) { + node_3d->set_as_top_level(false); + } } add_preview = true; } @@ -4579,8 +4609,12 @@ bool Node3DEditorViewport::_create_instance(Node *p_parent, const String &p_path } Transform3D new_tf = node3d->get_transform(); - new_tf.origin = parent_tf.affine_inverse().xform(preview_node_pos + node3d->get_position()); - new_tf.basis = parent_tf.affine_inverse().basis * new_tf.basis; + if (node3d->is_set_as_top_level()) { + new_tf.origin += preview_node_pos; + } else { + new_tf.origin = parent_tf.affine_inverse().xform(preview_node_pos + node3d->get_position()); + new_tf.basis = parent_tf.affine_inverse().basis * new_tf.basis; + } undo_redo->add_do_method(instantiated_scene, "set_transform", new_tf); } diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index c7e6420875..a8cade36fd 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -472,7 +472,7 @@ private: Point2 _get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_ev_mouse_motion) const; Vector3 _get_instance_position(const Point2 &p_pos) const; - static AABB _calculate_spatial_bounds(const Node3D *p_parent, const Node3D *p_top_level_parent = nullptr); + static AABB _calculate_spatial_bounds(const Node3D *p_parent, bool p_omit_top_level = false, const Transform3D *p_bounds_orientation = nullptr); Node *_sanitize_preview_node(Node *p_node) const; diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 8f646a7621..cc488ff340 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -3596,6 +3596,13 @@ void ThemeEditor::_theme_close_button_cbk() { } } +void ThemeEditor::_scene_closed(const String &p_path) { + if (theme.is_valid() && theme->is_built_in() && theme->get_path().get_slice("::", 0) == p_path) { + theme = Ref<Theme>(); + EditorNode::get_singleton()->hide_unused_editors(plugin); + } +} + void ThemeEditor::_add_preview_button_cbk() { preview_scene_dialog->popup_file_dialog(); } @@ -3679,7 +3686,10 @@ void ThemeEditor::_preview_control_picked(String p_class_name) { void ThemeEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_READY: { + EditorNode::get_singleton()->connect("scene_closed", callable_mp(this, &ThemeEditor::_scene_closed)); + } break; + case NOTIFICATION_THEME_CHANGED: { preview_tabs->add_theme_style_override("tab_selected", get_theme_stylebox(SNAME("ThemeEditorPreviewFG"), EditorStringName(EditorStyles))); preview_tabs->add_theme_style_override("tab_unselected", get_theme_stylebox(SNAME("ThemeEditorPreviewBG"), EditorStringName(EditorStyles))); @@ -3807,71 +3817,7 @@ void ThemeEditorPlugin::make_visible(bool p_visible) { } bool ThemeEditorPlugin::can_auto_hide() const { - Ref<Theme> edited_theme = theme_editor->theme; - if (edited_theme.is_null()) { - return true; - } - - Ref<Resource> edited_resource = Ref<Resource>(InspectorDock::get_inspector_singleton()->get_next_edited_object()); - if (edited_resource.is_null()) { - return true; - } - - // Don't hide if edited resource used by this theme. - Ref<StyleBox> sbox = edited_resource; - if (sbox.is_valid()) { - List<StringName> type_list; - edited_theme->get_stylebox_type_list(&type_list); - - for (const StringName &E : type_list) { - List<StringName> list; - edited_theme->get_stylebox_list(E, &list); - - for (const StringName &F : list) { - if (edited_theme->get_stylebox(F, E) == sbox) { - return false; - } - } - } - return true; - } - - Ref<Texture2D> tex = edited_resource; - if (tex.is_valid()) { - List<StringName> type_list; - edited_theme->get_icon_type_list(&type_list); - - for (const StringName &E : type_list) { - List<StringName> list; - edited_theme->get_icon_list(E, &list); - - for (const StringName &F : list) { - if (edited_theme->get_icon(F, E) == tex) { - return false; - } - } - } - return true; - } - - Ref<Font> fnt = edited_resource; - if (fnt.is_valid()) { - List<StringName> type_list; - edited_theme->get_font_type_list(&type_list); - - for (const StringName &E : type_list) { - List<StringName> list; - edited_theme->get_font_list(E, &list); - - for (const StringName &F : list) { - if (edited_theme->get_font(F, E) == fnt) { - return false; - } - } - } - return true; - } - return true; + return theme_editor->theme.is_null(); } ThemeEditorPlugin::ThemeEditorPlugin() { diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 1d009637b7..39dc8d154b 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -445,6 +445,7 @@ class ThemeEditor : public VBoxContainer { void _theme_save_button_cbk(bool p_save_as); void _theme_edit_button_cbk(); void _theme_close_button_cbk(); + void _scene_closed(const String &p_path); void _add_preview_button_cbk(); void _preview_scene_dialog_cbk(const String &p_path); diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 12b9761fd2..c6921699a4 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -874,6 +874,11 @@ void GenericTilePolygonEditor::_notification(int p_what) { button_expand->set_pressed_no_signal(false); } } break; + + case NOTIFICATION_READY: { + get_parent()->connect(SceneStringName(tree_exited), callable_mp(TileSetEditor::get_singleton(), &TileSetEditor::remove_expanded_editor)); + } break; + case NOTIFICATION_THEME_CHANGED: { button_expand->set_icon(get_editor_theme_icon(SNAME("DistractionFree"))); button_create->set_icon(get_editor_theme_icon(SNAME("CurveCreate"))); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 418f4e4932..f973367bed 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -104,7 +104,8 @@ void ProjectSettingsEditor::_update_advanced(bool p_is_advanced) { } void ProjectSettingsEditor::_advanced_toggled(bool p_button_pressed) { - EditorSettings::get_singleton()->set_project_metadata("project_settings", "advanced_mode", p_button_pressed); + EditorSettings::get_singleton()->set("_project_settings_advanced_mode", p_button_pressed); + EditorSettings::get_singleton()->save(); _update_advanced(p_button_pressed); } @@ -768,8 +769,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { set_ok_button_text(TTR("Close")); set_hide_on_ok(true); - bool use_advanced = EditorSettings::get_singleton()->get_project_metadata("project_settings", "advanced_mode", false); - + bool use_advanced = EDITOR_DEF("_project_settings_advanced_mode", false); if (use_advanced) { advanced->set_pressed(true); } diff --git a/platform/ios/detect.py b/platform/ios/detect.py index 989a7f21f3..20a3a996bc 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -2,7 +2,7 @@ import os import sys from typing import TYPE_CHECKING -from methods import detect_darwin_sdk_path, print_error +from methods import detect_darwin_sdk_path, print_error, print_warning if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -156,7 +156,7 @@ def configure(env: "SConsEnvironment"): env.Append(CPPDEFINES=["IOS_ENABLED", "UNIX_ENABLED", "COREAUDIO_ENABLED"]) if env["metal"] and env["arch"] != "arm64": - # Only supported on arm64, so skip it for x86_64 builds. + print_warning("Target architecture '{}' does not support the Metal rendering driver".format(env["arch"])) env["metal"] = False if env["metal"]: diff --git a/platform/macos/detect.py b/platform/macos/detect.py index 595a83ead3..a8968b592e 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -2,7 +2,7 @@ import os import sys from typing import TYPE_CHECKING -from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang, print_error +from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang, print_error, print_warning from platform_methods import detect_arch, detect_mvk if TYPE_CHECKING: @@ -249,7 +249,7 @@ def configure(env: "SConsEnvironment"): env.Append(LINKFLAGS=["-rpath", "@executable_path/../Frameworks", "-rpath", "@executable_path"]) if env["metal"] and env["arch"] != "arm64": - # Only supported on arm64, so skip it for x86_64 builds. + print_warning("Target architecture '{}' does not support the Metal rendering driver".format(env["arch"])) env["metal"] = False extra_frameworks = set() diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 9040693a6d..646757008a 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -653,7 +653,9 @@ void GraphEdit::remove_child_notify(Node *p_child) { minimap = nullptr; } else if (p_child == connections_layer) { connections_layer = nullptr; - WARN_PRINT("GraphEdit's connection_layer removed. This should not be done. If you like to remove all GraphElements from a GraphEdit node, do not simply remove all non-internal children but check their type since the connection layer has to be kept non-internal due to technical reasons."); + if (is_inside_tree()) { + WARN_PRINT("GraphEdit's connection_layer removed. This should not be done. If you like to remove all GraphElements from a GraphEdit node, do not simply remove all non-internal children but check their type since the connection layer has to be kept non-internal due to technical reasons."); + } } if (top_layer != nullptr && is_inside_tree()) { diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 2211bd76fc..1ac0e8b59f 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -547,7 +547,7 @@ PackedStringArray ScrollContainer::get_configuration_warnings() const { int found = 0; for (int i = 0; i < get_child_count(); i++) { - Control *c = as_sortable_control(get_child(i)); + Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); if (!c) { continue; } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 8238d54381..646cd9c70e 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -4034,25 +4034,25 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { } break; case MouseButton::WHEEL_UP: { - if (_scroll(false, -mb->get_factor() / 8)) { + if (_scroll(mb->is_shift_pressed(), -mb->get_factor() / 8)) { accept_event(); } } break; case MouseButton::WHEEL_DOWN: { - if (_scroll(false, mb->get_factor() / 8)) { + if (_scroll(mb->is_shift_pressed(), mb->get_factor() / 8)) { accept_event(); } } break; case MouseButton::WHEEL_LEFT: { - if (_scroll(true, -mb->get_factor() / 8)) { + if (_scroll(!mb->is_shift_pressed(), -mb->get_factor() / 8)) { accept_event(); } } break; case MouseButton::WHEEL_RIGHT: { - if (_scroll(true, mb->get_factor() / 8)) { + if (_scroll(!mb->is_shift_pressed(), mb->get_factor() / 8)) { accept_event(); } diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 106130872d..71d91b970e 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -302,11 +302,15 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro continue; } + Node *node = gr_nodes[i]; if (!(p_call_flags & GROUP_CALL_DEFERRED)) { Callable::CallError ce; - gr_nodes[i]->callp(p_function, p_args, p_argcount, ce); + node->callp(p_function, p_args, p_argcount, ce); + if (unlikely(ce.error != Callable::CallError::CALL_OK && ce.error != Callable::CallError::CALL_ERROR_INVALID_METHOD)) { + ERR_PRINT(vformat("Error calling group method on node \"%s\": %s.", node->get_name(), Variant::get_callable_error_text(Callable(node, p_function), p_args, p_argcount, ce))); + } } else { - MessageQueue::get_singleton()->push_callp(gr_nodes[i], p_function, p_args, p_argcount); + MessageQueue::get_singleton()->push_callp(node, p_function, p_args, p_argcount); } } @@ -316,11 +320,15 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro continue; } + Node *node = gr_nodes[i]; if (!(p_call_flags & GROUP_CALL_DEFERRED)) { Callable::CallError ce; - gr_nodes[i]->callp(p_function, p_args, p_argcount, ce); + node->callp(p_function, p_args, p_argcount, ce); + if (unlikely(ce.error != Callable::CallError::CALL_OK && ce.error != Callable::CallError::CALL_ERROR_INVALID_METHOD)) { + ERR_PRINT(vformat("Error calling group method on node \"%s\": %s.", node->get_name(), Variant::get_callable_error_text(Callable(node, p_function), p_args, p_argcount, ce))); + } } else { - MessageQueue::get_singleton()->push_callp(gr_nodes[i], p_function, p_args, p_argcount); + MessageQueue::get_singleton()->push_callp(node, p_function, p_args, p_argcount); } } } diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 5aa6bf718c..e234a81c88 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -612,29 +612,27 @@ Error ResourceLoaderText::load() { } } - if (ClassDB::has_property(res->get_class_name(), assign)) { - if (value.get_type() == Variant::ARRAY) { - Array set_array = value; - bool is_get_valid = false; - Variant get_value = res->get(assign, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::ARRAY) { - Array get_array = get_value; - if (!set_array.is_same_typed(get_array)) { - value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); - } + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = res->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); } } + } - if (value.get_type() == Variant::DICTIONARY) { - Dictionary set_dict = value; - bool is_get_valid = false; - Variant get_value = res->get(assign, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { - Dictionary get_dict = get_value; - if (!set_dict.is_same_typed(get_dict)) { - value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), - get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); - } + if (value.get_type() == Variant::DICTIONARY) { + Dictionary set_dict = value; + bool is_get_valid = false; + Variant get_value = res->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { + Dictionary get_dict = get_value; + if (!set_dict.is_same_typed(get_dict)) { + value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), + get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); } } } @@ -754,29 +752,27 @@ Error ResourceLoaderText::load() { } } - if (ClassDB::has_property(resource->get_class_name(), assign)) { - if (value.get_type() == Variant::ARRAY) { - Array set_array = value; - bool is_get_valid = false; - Variant get_value = resource->get(assign, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::ARRAY) { - Array get_array = get_value; - if (!set_array.is_same_typed(get_array)) { - value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); - } + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = resource->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); } } + } - if (value.get_type() == Variant::DICTIONARY) { - Dictionary set_dict = value; - bool is_get_valid = false; - Variant get_value = resource->get(assign, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { - Dictionary get_dict = get_value; - if (!set_dict.is_same_typed(get_dict)) { - value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), - get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); - } + if (value.get_type() == Variant::DICTIONARY) { + Dictionary set_dict = value; + bool is_get_valid = false; + Variant get_value = resource->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { + Dictionary get_dict = get_value; + if (!set_dict.is_same_typed(get_dict)) { + value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), + get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); } } } diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 28958f0393..979f590c4c 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1446,6 +1446,9 @@ void RendererCanvasRenderRD::CanvasShaderData::_create_pipeline(PipelineKey p_pi blend_state.attachments.push_back(attachment); + RD::PipelineMultisampleState multisample_state; + multisample_state.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_pipeline_key.framebuffer_format_id, 0); + // Convert the specialization from the key to pipeline specialization constants. Vector<RD::PipelineSpecializationConstant> specialization_constants; RD::PipelineSpecializationConstant sc; @@ -1457,7 +1460,7 @@ void RendererCanvasRenderRD::CanvasShaderData::_create_pipeline(PipelineKey p_pi RID shader_rid = get_shader(p_pipeline_key.variant, p_pipeline_key.ubershader); ERR_FAIL_COND(shader_rid.is_null()); - RID pipeline = RD::get_singleton()->render_pipeline_create(shader_rid, p_pipeline_key.framebuffer_format_id, p_pipeline_key.vertex_format_id, p_pipeline_key.render_primitive, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, dynamic_state_flags, 0, specialization_constants); + RID pipeline = RD::get_singleton()->render_pipeline_create(shader_rid, p_pipeline_key.framebuffer_format_id, p_pipeline_key.vertex_format_id, p_pipeline_key.render_primitive, RD::PipelineRasterizationState(), multisample_state, RD::PipelineDepthStencilState(), blend_state, dynamic_state_flags, 0, specialization_constants); ERR_FAIL_COND(pipeline.is_null()); pipeline_hash_map.add_compiled_pipeline(p_pipeline_key.hash(), pipeline); |