From e1cb65bedf1f8e1cf4a9f7ba41660983f998fc2a Mon Sep 17 00:00:00 2001 From: Shatyuka Date: Fri, 3 May 2024 06:36:51 +0800 Subject: [PATCH 01/26] Apply `WS_MINIMIZE` style on window creation --- platform/windows/display_server_windows.cpp | 28 +++++++++++++++------ platform/windows/display_server_windows.h | 4 ++- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index f101d85d583..33c4ef9dffc 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1339,6 +1339,7 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod rendering_device->screen_create(window_id); } #endif + wd.initialized = true; return window_id; } @@ -1895,7 +1896,7 @@ Size2i DisplayServerWindows::window_get_size_with_decorations(WindowID p_window) return Size2(); } -void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) { +void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_initialized, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_minimized, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) { // Windows docs for window styles: // https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles // https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles @@ -1904,12 +1905,16 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre r_style_ex = WS_EX_WINDOWEDGE; if (p_main_window) { r_style_ex |= WS_EX_APPWINDOW; - r_style |= WS_VISIBLE; + if (p_initialized) { + r_style |= WS_VISIBLE; + } } if (p_fullscreen || p_borderless) { r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past. - if (p_maximized) { + if (p_minimized) { + r_style |= WS_MINIMIZE; + } else if (p_maximized) { r_style |= WS_MAXIMIZE; } if (!p_fullscreen) { @@ -1924,13 +1929,19 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre } } else { if (p_resizable) { - if (p_maximized) { + if (p_minimized) { + r_style = WS_OVERLAPPEDWINDOW | WS_MINIMIZE; + } else if (p_maximized) { r_style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE; } else { r_style = WS_OVERLAPPEDWINDOW; } } else { - r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + if (p_minimized) { + r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MINIMIZE; + } else { + r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + } } } @@ -1938,7 +1949,7 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre r_style_ex |= WS_EX_TOPMOST | WS_EX_NOACTIVATE; } - if (!p_borderless && !p_no_activate_focus) { + if (!p_borderless && !p_no_activate_focus && p_initialized) { r_style |= WS_VISIBLE; } @@ -1955,7 +1966,7 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain DWORD style = 0; DWORD style_ex = 0; - _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.maximized, wd.maximized_fs, wd.no_focus || wd.is_popup, style, style_ex); + _get_window_style(p_window == MAIN_WINDOW_ID, wd.initialized, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.minimized, wd.maximized, wd.maximized_fs, wd.no_focus || wd.is_popup, style, style_ex); SetWindowLongPtr(wd.hWnd, GWL_STYLE, style); SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex); @@ -5088,7 +5099,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, DWORD dwExStyle; DWORD dwStyle; - _get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle); + _get_window_style(window_id_counter == MAIN_WINDOW_ID, false, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MINIMIZED, p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle); RECT WindowRect; @@ -5793,6 +5804,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } } + windows[MAIN_WINDOW_ID].initialized = true; show_window(MAIN_WINDOW_ID); #if defined(RD_ENABLED) diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 80f6061348a..b549e377407 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -445,6 +445,8 @@ class DisplayServerWindows : public DisplayServer { bool is_popup = false; Rect2i parent_safe_rect; + + bool initialized = false; }; JoypadWindows *joypad = nullptr; @@ -472,7 +474,7 @@ class DisplayServerWindows : public DisplayServer { HashMap indicators; void _send_window_event(const WindowData &wd, WindowEvent p_event); - void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex); + void _get_window_style(bool p_main_window, bool p_initialized, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_minimized, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex); MouseMode mouse_mode; int restore_mouse_trails = 0; From 14f6c816bac06580dc150b8dda111005131656fb Mon Sep 17 00:00:00 2001 From: Robert Yevdokimov Date: Sun, 18 Feb 2024 13:38:45 -0500 Subject: [PATCH 02/26] Offset drag instantiated scenes that result in a collision by its bounds to prevent overlap Co-Authored-By: Robbie Cooper --- editor/plugins/node_3d_editor_plugin.cpp | 56 +++++++++++++++++++----- editor/plugins/node_3d_editor_plugin.h | 2 +- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index d211bd85884..737f8e3f981 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -4104,8 +4104,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; @@ -4133,18 +4156,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(p_parent); if (visual_instance) { @@ -4155,9 +4181,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(p_parent->get_child(i)); - if (child) { - AABB child_bounds = _calculate_spatial_bounds(child, p_top_level_parent); + const Node3D *child = Object::cast_to(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); } } @@ -4208,6 +4234,10 @@ void Node3DEditorViewport::_create_preview_node(const Vector &files) con if (instance) { instance = _sanitize_preview_node(instance); preview_node->add_child(instance); + Node3D *node_3d = Object::cast_to(instance); + if (node_3d) { + node_3d->set_as_top_level(false); + } } add_preview = true; } @@ -4428,8 +4458,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 ebdf9517730..8b7e29709ec 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -435,7 +435,7 @@ class Node3DEditorViewport : public Control { Point2 _get_warped_mouse_motion(const Ref &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; From 2470eedd614c7a902c8ff0d6d013da014f4267bb Mon Sep 17 00:00:00 2001 From: kobewi Date: Sun, 28 Jul 2024 20:31:36 +0200 Subject: [PATCH 03/26] Call restart_editor() in RUN_PROJECT_MANAGER --- editor/editor_node.cpp | 33 ++++++++++----------------------- editor/editor_node.h | 2 +- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 8b6d316dd16..7a9a265ffc4 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1961,7 +1961,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()) { @@ -1969,22 +1969,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 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); @@ -3391,23 +3394,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 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 4d55eaf1b2a..10e4447bdce 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -917,7 +917,7 @@ class EditorNode : public Node { void save_scene_list(const HashSet &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); From e2c03469fb944d5d2489acc14714104ad3166bce Mon Sep 17 00:00:00 2001 From: kobewi Date: Thu, 1 Aug 2024 20:30:16 +0200 Subject: [PATCH 04/26] Close expanded tile editor when inspector updates --- editor/plugins/tiles/tile_data_editors.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index f985bbc6290..c97cd801362 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -859,6 +859,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"))); From 5cf9afb0c0409c1115c5b090371c6c81379315d9 Mon Sep 17 00:00:00 2001 From: kobewi Date: Sun, 4 Aug 2024 22:31:10 +0200 Subject: [PATCH 05/26] Don't fold resources when child of main inspector exits --- editor/editor_properties.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index fdb4ec170b8..f00f5602cce 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -3417,7 +3417,8 @@ 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; From b67eb68e5bee8c5bcf3d3589be06ab569f7e3e8b Mon Sep 17 00:00:00 2001 From: kobewi Date: Thu, 15 Aug 2024 08:17:40 +0200 Subject: [PATCH 06/26] Misc code cleanup in EditorFileDialog --- editor/gui/editor_file_dialog.cpp | 44 +++++++++++++++++-------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp index afc6d58d631..21eb60255fd 100644 --- a/editor/gui/editor_file_dialog.cpp +++ b/editor/gui/editor_file_dialog.cpp @@ -1522,7 +1522,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(); @@ -1538,8 +1538,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; } @@ -1551,7 +1554,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(); } @@ -1562,7 +1565,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); @@ -1589,7 +1592,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("/")) { @@ -1599,13 +1602,12 @@ void EditorFileDialog::_favorite_pressed() { Vector 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; } @@ -1625,31 +1627,30 @@ void EditorFileDialog::_favorite_pressed() { void EditorFileDialog::_update_recent() { recent->clear(); - bool res = (access == ACCESS_RESOURCES); + bool access_resources = (access == ACCESS_RESOURCES); Vector recentd = EditorSettings::get_singleton()->get_recent_dirs(); Vector recentd_paths; Vector 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]); @@ -1663,7 +1664,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) { From 42e5c5b41c6a60fe1c21e0edd2e1c9b70dd87e57 Mon Sep 17 00:00:00 2001 From: kobewi Date: Wed, 21 Aug 2024 10:44:45 +0200 Subject: [PATCH 07/26] Allow horizontal scrolling in Tree using Shift --- scene/gui/tree.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 46fcdcf7f6c..55ce7904f35 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3957,25 +3957,25 @@ void Tree::gui_input(const Ref &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(); } From b01c2ef42db08dc299220ddc80ca2421f4ad2e9c Mon Sep 17 00:00:00 2001 From: kobewi Date: Thu, 5 Sep 2024 18:52:44 +0200 Subject: [PATCH 08/26] Globally remember advanced toggle in project settings --- editor/project_settings_editor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 489fbb037ff..19d3f64f623 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); general_settings_inspector->set_restrict_to_basic_settings(!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); } From 971e154588f502de2c7789da43c3bbce45bec8bc Mon Sep 17 00:00:00 2001 From: David Snopek Date: Tue, 17 Sep 2024 09:55:30 -0500 Subject: [PATCH 09/26] GDExtension: Allow directly getting `ObjectID` from `Variant` --- core/extension/gdextension_interface.cpp | 9 +++++++++ core/extension/gdextension_interface.h | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index ddf90f61307..66b01611602 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -507,6 +507,14 @@ static GDExtensionBool gdextension_variant_has_key(GDExtensionConstVariantPtr p_ return ret; } +static GDObjectInstanceID gdextension_variant_get_object_instance_id(GDExtensionConstVariantPtr p_self) { + const Variant *self = (const Variant *)p_self; + if (likely(self->get_type() == Variant::OBJECT)) { + return self->operator ObjectID(); + } + return 0; +} + static void gdextension_variant_get_type_name(GDExtensionVariantType p_type, GDExtensionUninitializedVariantPtr r_ret) { String name = Variant::get_type_name((Variant::Type)p_type); memnew_placement(r_ret, String(name)); @@ -1610,6 +1618,7 @@ void gdextension_setup_interface() { REGISTER_INTERFACE_FUNC(variant_has_method); REGISTER_INTERFACE_FUNC(variant_has_member); REGISTER_INTERFACE_FUNC(variant_has_key); + REGISTER_INTERFACE_FUNC(variant_get_object_instance_id); REGISTER_INTERFACE_FUNC(variant_get_type_name); REGISTER_INTERFACE_FUNC(variant_can_convert); REGISTER_INTERFACE_FUNC(variant_can_convert_strict); diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 9e3ce25698e..374dbfd0711 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -1307,6 +1307,21 @@ typedef GDExtensionBool (*GDExtensionInterfaceVariantHasMember)(GDExtensionVaria */ typedef GDExtensionBool (*GDExtensionInterfaceVariantHasKey)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionBool *r_valid); +/** + * @name variant_get_object_instance_id + * @since 4.4 + * + * Gets the object instance ID from a variant of type GDEXTENSION_VARIANT_TYPE_OBJECT. + * + * If the variant isn't of type GDEXTENSION_VARIANT_TYPE_OBJECT, then zero will be returned. + * The instance ID will be returned even if the object is no longer valid - use `object_get_instance_by_id()` to check if the object is still valid. + * + * @param p_self A pointer to the Variant. + * + * @return The instance ID for the contained object. + */ +typedef GDObjectInstanceID (*GDExtensionInterfaceVariantGetObjectInstanceId)(GDExtensionConstVariantPtr p_self); + /** * @name variant_get_type_name * @since 4.1 From 22ccfc5a392811bedf605a1462f58019fa807ea7 Mon Sep 17 00:00:00 2001 From: kobewi Date: Fri, 20 Sep 2024 17:20:03 +0200 Subject: [PATCH 10/26] Print error on invalid call_group() calls --- scene/main/scene_tree.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 106130872db..71d91b970ee 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); } } } From 59c66585d5a51fc0b9ef79d72a461888cff5831a Mon Sep 17 00:00:00 2001 From: kobewi Date: Sat, 21 Sep 2024 19:53:02 +0200 Subject: [PATCH 11/26] Fix ScrollContainer configuration warnings --- scene/gui/scroll_container.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 2211bd76fc9..1ac0e8b59fa 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; } From 866c50a9260be4f92f6131384c9cfccabb8df7a7 Mon Sep 17 00:00:00 2001 From: kobewi Date: Sat, 21 Sep 2024 20:16:45 +0200 Subject: [PATCH 12/26] Add submenu support to EditorContextMenuPlugin --- doc/classes/EditorContextMenuPlugin.xml | 18 +++++++++++++++ editor/plugins/editor_context_menu_plugin.cpp | 22 ++++++++++++++++--- editor/plugins/editor_context_menu_plugin.h | 2 ++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/doc/classes/EditorContextMenuPlugin.xml b/doc/classes/EditorContextMenuPlugin.xml index 71c4ca0f9b9..fb90a2a5cd6 100644 --- a/doc/classes/EditorContextMenuPlugin.xml +++ b/doc/classes/EditorContextMenuPlugin.xml @@ -47,6 +47,24 @@ [/codeblock] + + + + + + + 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] + + diff --git a/editor/plugins/editor_context_menu_plugin.cpp b/editor/plugins/editor_context_menu_plugin.cpp index 0648327faba..b635816bd97 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 &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())); ClassDB::bind_method(D_METHOD("add_context_menu_item_from_shortcut", "name", "shortcut", "icon"), &EditorContextMenuPlugin::add_context_menu_item_from_shortcut, DEFVAL(Ref())); + ClassDB::bind_method(D_METHOD("add_context_submenu_item", "name", "menu", "icon"), &EditorContextMenuPlugin::add_context_submenu_item, DEFVAL(Ref())); 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 0232d254ba5..86c67dedda1 100644 --- a/editor/plugins/editor_context_menu_plugin.h +++ b/editor/plugins/editor_context_menu_plugin.h @@ -65,6 +65,7 @@ class EditorContextMenuPlugin : public RefCounted { Callable callable; Ref icon; Ref shortcut; + PopupMenu *submenu = nullptr; }; HashMap context_menu_items; HashMap, Callable> context_menu_shortcuts; @@ -80,6 +81,7 @@ class EditorContextMenuPlugin : public RefCounted { void add_menu_shortcut(const Ref &p_shortcut, const Callable &p_callable); void add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref &p_texture); void add_context_menu_item_from_shortcut(const String &p_name, const Ref &p_shortcut, const Ref &p_texture); + void add_context_submenu_item(const String &p_name, PopupMenu *p_menu, const Ref &p_texture); }; VARIANT_ENUM_CAST(EditorContextMenuPlugin::ContextMenuSlot); From 0ad1820b1a7af28f32773e25a938931f3f8fb5e4 Mon Sep 17 00:00:00 2001 From: Sander Date: Sun, 22 Sep 2024 02:00:53 +0300 Subject: [PATCH 13/26] Metal: implement texture_create_from_extension Parameters p_type, p_format, p_array_layers, p_depth_stencil are ignored - MTLTexture (and the callee) already have this information and is only relevant when reinterpreting or remaping the texture in different ways. --- drivers/metal/rendering_device_driver_metal.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm index 9d691a0d230..0f7faaddf08 100644 --- a/drivers/metal/rendering_device_driver_metal.mm +++ b/drivers/metal/rendering_device_driver_metal.mm @@ -358,7 +358,11 @@ _FORCE_INLINE_ MTLSize mipmapLevelSizeFromSize(MTLSize p_size, NSUInteger p_leve } RDD::TextureID RenderingDeviceDriverMetal::texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil) { - ERR_FAIL_V_MSG(RDD::TextureID(), "not implemented"); + id obj = (__bridge id)(void *)(uintptr_t)p_native_texture; + + // We only need to create a RDD::TextureID for an existing, natively-provided texture. + + return rid::make(obj); } RDD::TextureID RenderingDeviceDriverMetal::texture_create_shared(TextureID p_original_texture, const TextureView &p_view) { From abf9d245208542ccf883f6b61f0d3be486dc9447 Mon Sep 17 00:00:00 2001 From: Juan Date: Mon, 23 Sep 2024 15:07:00 +0200 Subject: [PATCH 14/26] Make internal unique scene resource ID deterministic Changes the Resource::generate_scene_unique_id() to be deterministic and seedable. Fixes #97110 --- core/io/resource.cpp | 33 ++++++++++++++++-------- core/io/resource.h | 1 + core/io/resource_format_binary.cpp | 2 ++ scene/resources/resource_format_text.cpp | 2 ++ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 5f8a4b85a4e..0ff4fbe490c 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -99,31 +99,42 @@ void Resource::set_path_cache(const String &p_path) { GDVIRTUAL_CALL(_set_path_cache, p_path); } +static thread_local RandomPCG unique_id_gen(0, RandomPCG::DEFAULT_INC); + +void Resource::seed_scene_unique_id(uint32_t p_seed) { + unique_id_gen.seed(p_seed); +} + String Resource::generate_scene_unique_id() { // Generate a unique enough hash, but still user-readable. // If it's not unique it does not matter because the saver will try again. - OS::DateTime dt = OS::get_singleton()->get_datetime(); - uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec()); - hash = hash_murmur3_one_32(dt.year, hash); - hash = hash_murmur3_one_32(dt.month, hash); - hash = hash_murmur3_one_32(dt.day, hash); - hash = hash_murmur3_one_32(dt.hour, hash); - hash = hash_murmur3_one_32(dt.minute, hash); - hash = hash_murmur3_one_32(dt.second, hash); - hash = hash_murmur3_one_32(Math::rand(), hash); + if (unique_id_gen.get_seed() == 0) { + OS::DateTime dt = OS::get_singleton()->get_datetime(); + uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec()); + hash = hash_murmur3_one_32(dt.year, hash); + hash = hash_murmur3_one_32(dt.month, hash); + hash = hash_murmur3_one_32(dt.day, hash); + hash = hash_murmur3_one_32(dt.hour, hash); + hash = hash_murmur3_one_32(dt.minute, hash); + hash = hash_murmur3_one_32(dt.second, hash); + hash = hash_murmur3_one_32(Math::rand(), hash); + unique_id_gen.seed(hash); + } + + uint32_t random_num = unique_id_gen.rand(); static constexpr uint32_t characters = 5; static constexpr uint32_t char_count = ('z' - 'a'); static constexpr uint32_t base = char_count + ('9' - '0'); String id; for (uint32_t i = 0; i < characters; i++) { - uint32_t c = hash % base; + uint32_t c = random_num % base; if (c < char_count) { id += String::chr('a' + c); } else { id += String::chr('0' + (c - char_count)); } - hash /= base; + random_num /= base; } return id; diff --git a/core/io/resource.h b/core/io/resource.h index 8966c0233ca..015f7ad197b 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -114,6 +114,7 @@ class Resource : public RefCounted { virtual void set_path_cache(const String &p_path); // Set raw path without involving resource cache. _FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.contains("::") || path_cache.begins_with("local://"); } + static void seed_scene_unique_id(uint32_t p_seed); static String generate_scene_unique_id(); void set_scene_unique_id(const String &p_id); String get_scene_unique_id() const; diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index b4826c356e0..3bfa0223828 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -2136,6 +2136,8 @@ static String _resource_get_class(Ref p_resource) { } Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref &p_resource, uint32_t p_flags) { + Resource::seed_scene_unique_id(p_path.hash()); + Error err; Ref f; if (p_flags & ResourceSaver::FLAG_COMPRESS) { diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index d531eea3112..5aa6bf718cb 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -1708,6 +1708,8 @@ static String _resource_get_class(Ref p_resource) { } Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref &p_resource, uint32_t p_flags) { + Resource::seed_scene_unique_id(p_path.hash()); // Seeding for save path should make it deterministic for importers. + if (p_path.ends_with(".tscn")) { packed_scene = p_resource; } From 66d2b0fc6a7f656f37e65a93eacc77642602aca9 Mon Sep 17 00:00:00 2001 From: kobewi Date: Fri, 27 Sep 2024 16:32:27 +0200 Subject: [PATCH 15/26] Fix closing Theme Editor not actually closing it --- editor/editor_node.cpp | 4 +- editor/editor_properties.cpp | 8 +++ editor/editor_properties.h | 2 + editor/editor_resource_picker.cpp | 4 ++ editor/editor_resource_picker.h | 1 + editor/plugins/theme_editor_plugin.cpp | 78 ++++---------------------- editor/plugins/theme_editor_plugin.h | 1 + 7 files changed, 30 insertions(+), 68 deletions(-) diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 665255b9b24..41148f83325 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2386,7 +2386,7 @@ void EditorNode::hide_unused_editors(const Object *p_editing_owner) { // This is to sweep properties that were removed from the inspector. List to_remove; for (KeyValue> &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(context); @@ -2395,7 +2395,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()) { diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 0fb57ce40e7..fd2329dfe2e 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(to_node)) { @@ -3357,6 +3361,10 @@ void EditorPropertyResource::_notification(int p_what) { } } +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 2ec78cdb448..004630da3e2 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 a81db5fdaa1..4052d0865a1 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -956,6 +956,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()); diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index 05e392da2c7..92df018a59f 100644 --- a/editor/editor_resource_picker.h +++ b/editor/editor_resource_picker.h @@ -137,6 +137,7 @@ class EditorResourcePicker : public HBoxContainer { 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/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 8f646a76212..cc488ff3402 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(); + 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 edited_theme = theme_editor->theme; - if (edited_theme.is_null()) { - return true; - } - - Ref edited_resource = Ref(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 sbox = edited_resource; - if (sbox.is_valid()) { - List type_list; - edited_theme->get_stylebox_type_list(&type_list); - - for (const StringName &E : type_list) { - List 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 tex = edited_resource; - if (tex.is_valid()) { - List type_list; - edited_theme->get_icon_type_list(&type_list); - - for (const StringName &E : type_list) { - List 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 fnt = edited_resource; - if (fnt.is_valid()) { - List type_list; - edited_theme->get_font_type_list(&type_list); - - for (const StringName &E : type_list) { - List 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 1d009637b7a..39dc8d154bb 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); From 4c5094a2fe31c366b0d6fe4d0ab44a7c90d753ff Mon Sep 17 00:00:00 2001 From: "Yevhen Babiichuk (DustDFG)" Date: Fri, 27 Sep 2024 20:30:51 +0300 Subject: [PATCH 16/26] Use dedicated `print_error` method for colored output for unsupported drivers Signed-off-by: Yevhen Babiichuk (DustDFG) --- drivers/SCsub | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/SCsub b/drivers/SCsub index 219c4451eef..e0bfa138f58 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -1,6 +1,8 @@ #!/usr/bin/env python from misc.utility.scons_hints import * +from methods import print_error + Import("env") env.drivers_sources = [] @@ -20,7 +22,7 @@ if env["platform"] == "windows": SConscript("backtrace/SCsub") if env["xaudio2"]: if "xaudio2" not in supported: - print("Target platform '{}' does not support the XAudio2 audio driver. Aborting.".format(env["platform"])) + print_error("Target platform '{}' does not support the XAudio2 audio driver".format(env["platform"])) Exit(255) SConscript("xaudio2/SCsub") @@ -34,7 +36,7 @@ if env["vulkan"]: SConscript("vulkan/SCsub") if env["d3d12"]: if "d3d12" not in supported: - print("Target platform '{}' does not support the D3D12 rendering driver. Aborting.".format(env["platform"])) + print_error("Target platform '{}' does not support the D3D12 rendering driver".format(env["platform"])) Exit(255) SConscript("d3d12/SCsub") if env["opengl3"]: @@ -43,7 +45,7 @@ if env["opengl3"]: SConscript("egl/SCsub") if env["metal"]: if "metal" not in supported: - print("Target platform '{}' does not support the Metal rendering driver. Aborting.".format(env["platform"])) + print_error("Target platform '{}' does not support the Metal rendering driver".format(env["platform"])) Exit(255) SConscript("metal/SCsub") From 1abcfdda851610d9f72985cc0b8ce4657f523cef Mon Sep 17 00:00:00 2001 From: "Yevhen Babiichuk (DustDFG)" Date: Sat, 28 Sep 2024 08:28:33 +0300 Subject: [PATCH 17/26] Replace comments with printed warning for Metal on x86_64 Signed-off-by: Yevhen Babiichuk (DustDFG) --- platform/ios/detect.py | 4 ++-- platform/macos/detect.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/ios/detect.py b/platform/ios/detect.py index 989a7f21f39..20a3a996bc6 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -2,7 +2,7 @@ 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 e35423d41f0..9eb5d41ab74 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -2,7 +2,7 @@ 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: @@ -241,7 +241,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() From c1dc59f7134bc9f92daba92f1ed530db73101a5c Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Sun, 15 May 2022 00:02:52 +0200 Subject: [PATCH 18/26] Disable unused Basis Universal features to reduce binary size --- modules/basis_universal/SCsub | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/modules/basis_universal/SCsub b/modules/basis_universal/SCsub index 0142317e1ef..9bea0a0ca99 100644 --- a/modules/basis_universal/SCsub +++ b/modules/basis_universal/SCsub @@ -50,6 +50,26 @@ if env.dev_build: env_thirdparty = env_basisu.Clone() env_thirdparty.disable_warnings() + +# Disable unneeded features to reduce binary size. +# +env_thirdparty.Append( + CPPDEFINES=[ + # Storage formats. + # Godot only implements `.basis` support through basis_universal. + # Support for `.ktx` files are implemented with a direct libktx implementation. + # Building the encoder requires `BASISD_SUPPORT_KTX2` to be enabled, + # so we can only disable Zstandard compression for `.ktx` files + # (this is not used in `.basis` files). + ("BASISD_SUPPORT_KTX2_ZSTD", 0), + # GPU compression formats. + ("BASISD_SUPPORT_ATC", 0), # Proprietary Adreno format not supported by Godot. + ("BASISD_SUPPORT_FXT1", 0), # Legacy format not supported by Godot. + ("BASISD_SUPPORT_PVRTC1", 0), # Legacy format not supported by Godot. + ("BASISD_SUPPORT_PVRTC2", 0), # Legacy format not supported by Godot. + ] +) + if env.editor_build: env_thirdparty.Append(CPPDEFINES=["BASISU_NO_IMG_LOADERS"]) env_thirdparty.add_source_files(thirdparty_obj, encoder_sources) From f84f7346969f0a18c24c53e6adced39af4fc0777 Mon Sep 17 00:00:00 2001 From: Mounir Tohami <53877170+WhalesState@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:37:33 +0000 Subject: [PATCH 19/26] Expose `LineEdit` `edit` and `unedit` methods. --- doc/classes/LineEdit.xml | 15 +++++++++- editor/code_editor.cpp | 2 ++ scene/gui/color_picker.cpp | 16 ++++++++++- scene/gui/line_edit.cpp | 56 ++++++++++++++++++++++---------------- scene/gui/line_edit.h | 5 ++-- 5 files changed, 66 insertions(+), 28 deletions(-) diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index 41f42392dea..3e0c328dcb9 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -8,7 +8,7 @@ - When the [LineEdit] control is focused using the keyboard arrow keys, it will only gain focus and not enter edit mode. - To enter edit mode, click on the control with the mouse or press the [code]ui_text_submit[/code] action (by default [kbd]Enter[/kbd] or [kbd]Kp Enter[/kbd]). - To exit edit mode, press [code]ui_text_submit[/code] or [code]ui_cancel[/code] (by default [kbd]Escape[/kbd]) actions. - - Check [method is_editing] and [signal editing_toggled] for more information. + - Check [method edit], [method unedit], [method is_editing], and [signal editing_toggled] for more information. [b]Important:[/b] - Focusing the [LineEdit] with [code]ui_focus_next[/code] (by default [kbd]Tab[/kbd]) or [code]ui_focus_prev[/code] (by default [kbd]Shift + Tab[/kbd]) or [method Control.grab_focus] still enters edit mode (for compatibility). [LineEdit] features many built-in shortcuts that are always available ([kbd]Ctrl[/kbd] here maps to [kbd]Cmd[/kbd] on macOS): @@ -75,6 +75,13 @@ Clears the current selection. + + + + Allows entering edit mode whether the [LineEdit] is focused or not. + Use [method Callable.call_deferred] if you want to enter edit mode on [signal text_submitted]. + + @@ -223,6 +230,12 @@ Selects the whole [String]. + + + + Allows exiting edit mode while preserving focus. + + diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 07547fee8a0..88a32b1a6da 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -644,6 +644,8 @@ void FindReplaceBar::_search_text_submitted(const String &p_text) { } else { search_next(); } + + callable_mp(search_text, &LineEdit::edit).call_deferred(); } void FindReplaceBar::_replace_text_submitted(const String &p_text) { diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 002a738b839..fe4c91cb569 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -245,7 +245,21 @@ void ColorPicker::finish_shaders() { } void ColorPicker::set_focus_on_line_edit() { - callable_mp((Control *)c_text, &Control::grab_focus).call_deferred(); + bool has_hardware_keyboard = true; +#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED) + has_hardware_keyboard = DisplayServer::get_singleton()->has_hardware_keyboard(); +#endif // ANDROID_ENABLED || IOS_ENABLED + if (has_hardware_keyboard) { + callable_mp((Control *)c_text, &Control::grab_focus).call_deferred(); + } else { + // A hack to avoid showing the virtual keyboard when the ColorPicker window popups and + // no hardware keyboard is detected on Android and IOS. + // This will only focus the LineEdit without editing, the virtual keyboard will only be visible when + // we touch the LineEdit to enter edit mode. + callable_mp(c_text, &LineEdit::set_editable).call_deferred(false); + callable_mp((Control *)c_text, &Control::grab_focus).call_deferred(); + callable_mp(c_text, &LineEdit::set_editable).call_deferred(true); + } } void ColorPicker::_update_controls() { diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 6e5b555cdfe..99678051346 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -45,28 +45,37 @@ #include "editor/editor_settings.h" #endif -void LineEdit::_edit() { +void LineEdit::edit() { if (!is_inside_tree()) { return; } if (!has_focus()) { grab_focus(); + return; } if (!editable || editing) { return; } + if (select_all_on_focus) { + if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { + // Select all when the mouse button is up. + pending_select_all_on_focus = true; + } else { + select_all(); + } + } + editing = true; _validate_caret_can_draw(); show_virtual_keyboard(); queue_redraw(); - emit_signal(SNAME("editing_toggled"), true); } -void LineEdit::_unedit() { +void LineEdit::unedit() { if (!editing) { return; } @@ -84,8 +93,6 @@ void LineEdit::_unedit() { if (deselect_on_focus_loss_enabled && !selection.drag_attempt) { deselect(); } - - emit_signal(SNAME("editing_toggled"), false); } bool LineEdit::is_editing() const { @@ -390,7 +397,8 @@ void LineEdit::gui_input(const Ref &p_event) { } if (editable && !editing) { - _edit(); + edit(); + emit_signal(SNAME("editing_toggled"), true); } accept_event(); @@ -406,7 +414,8 @@ void LineEdit::gui_input(const Ref &p_event) { set_caret_at_pixel_pos(b->get_position().x); if (!editing) { - _edit(); + edit(); + emit_signal(SNAME("editing_toggled"), true); } if (!paste_buffer.is_empty()) { @@ -506,7 +515,8 @@ void LineEdit::gui_input(const Ref &p_event) { } if (editable && !editing) { - _edit(); + edit(); + emit_signal(SNAME("editing_toggled"), true); return; } queue_redraw(); @@ -599,7 +609,9 @@ void LineEdit::gui_input(const Ref &p_event) { } if (editable && !editing && k->is_action_pressed("ui_text_submit", false)) { - _edit(); + edit(); + emit_signal(SNAME("editing_toggled"), true); + accept_event(); return; } @@ -734,7 +746,8 @@ void LineEdit::gui_input(const Ref &p_event) { } if (editing) { - _unedit(); + unedit(); + emit_signal(SNAME("editing_toggled"), false); } accept_event(); @@ -743,7 +756,8 @@ void LineEdit::gui_input(const Ref &p_event) { if (k->is_action("ui_cancel")) { if (editing) { - _unedit(); + unedit(); + emit_signal(SNAME("editing_toggled"), false); } accept_event(); @@ -1332,24 +1346,17 @@ void LineEdit::_notification(int p_what) { } break; case NOTIFICATION_FOCUS_ENTER: { - if (select_all_on_focus) { - if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { - // Select all when the mouse button is up. - pending_select_all_on_focus = true; - } else { - select_all(); - } - } - // Only allow editing if the LineEdit is not focused with arrow keys. if (!(Input::get_singleton()->is_action_pressed("ui_up") || Input::get_singleton()->is_action_pressed("ui_down") || Input::get_singleton()->is_action_pressed("ui_left") || Input::get_singleton()->is_action_pressed("ui_right"))) { - _edit(); + edit(); + emit_signal(SNAME("editing_toggled"), true); } } break; case NOTIFICATION_FOCUS_EXIT: { if (editing) { - _unedit(); + unedit(); + emit_signal(SNAME("editing_toggled"), false); } } break; @@ -2138,7 +2145,8 @@ void LineEdit::set_editable(bool p_editable) { editable = p_editable; if (!editable && editing) { - _unedit(); + unedit(); + emit_signal(SNAME("editing_toggled"), false); } _validate_caret_can_draw(); @@ -2759,6 +2767,8 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &LineEdit::set_horizontal_alignment); ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &LineEdit::get_horizontal_alignment); + ClassDB::bind_method(D_METHOD("edit"), &LineEdit::edit); + ClassDB::bind_method(D_METHOD("unedit"), &LineEdit::unedit); ClassDB::bind_method(D_METHOD("is_editing"), &LineEdit::is_editing); ClassDB::bind_method(D_METHOD("clear"), &LineEdit::clear); ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1)); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index ac7436646b5..9253dd87119 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -207,9 +207,6 @@ class LineEdit : public Control { float base_scale = 1.0; } theme_cache; - void _edit(); - void _unedit(); - void _close_ime_window(); void _update_ime_window_position(); @@ -265,6 +262,8 @@ class LineEdit : public Control { virtual void gui_input(const Ref &p_event) override; public: + void edit(); + void unedit(); bool is_editing() const; bool has_ime_text() const; From 4c520e94008227e315fceb8922adc48af8fa7532 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Thu, 3 Oct 2024 23:03:19 +0300 Subject: [PATCH 20/26] [iOS] Add support for dark and tinted icon versions. --- .../doc_classes/EditorExportPlatformIOS.xml | 125 +++++++++- platform/ios/export/export_plugin.cpp | 233 +++++++++++------- 2 files changed, 270 insertions(+), 88 deletions(-) diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml index 1d4a944dc48..9e6f191faad 100644 --- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml +++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml @@ -96,39 +96,156 @@ App Store application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. - - Home screen application icon file on iPad (1x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + App Store application icon file, dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + App Store application icon file, tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Base application icon used to generate other icons. If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Base application icon used to generate other icons, dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Base application icon used to generate other icons, tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + iOS application 64x64 icon file (2x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + iOS application 64x64 icon file (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + iOS application 64x64 icon file (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + iOS application 68x68 icon file (2x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + iOS application 68x68 icon file (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + iOS application 68x68 icon file (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + iOS application 64x64 icon file (3x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + iOS application 64x64 icon file (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + iOS application 64x64 icon file (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. Home screen application icon file on iPad (2x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + Home screen application icon file on iPad (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Home screen application icon file on iPad (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + Home screen application icon file on iPad (3x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + Home screen application icon file on iPad (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Home screen application icon file on iPad (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + Home screen application icon file on iPhone (2x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + Home screen application icon file on iPhone (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Home screen application icon file on iPhone (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + Home screen application icon file on iPhone (3x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + Home screen application icon file on iPhone (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Home screen application icon file on iPhone (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + Notification icon file on iPad and iPhone (2x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + Notification icon file on iPad and iPhone (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Notification icon file on iPad and iPhone (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + Notification icon file on iPhone (3x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + Notification icon file on iPhone (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Notification icon file on iPhone (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Notification icon file on iPad and iPhone (2x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Notification icon file on iPad and iPhone (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Notification icon file on iPad and iPhone (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Notification icon file on iPad and iPhone (3x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Notification icon file on iPad and iPhone (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Notification icon file on iPad and iPhone (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + Application settings icon file on iPad and iPhone (2x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + Application settings icon file on iPad and iPhone (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Application settings icon file on iPad and iPhone (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + Application settings icon file on iPhone (3x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. - - Spotlight icon file on iPad (1x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + Application settings icon file on iPhone (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Application settings icon file on iPhone (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. Spotlight icon file on iPad and iPhone (2x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + Spotlight icon file on iPad and iPhone (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Spotlight icon file on iPad and iPhone (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Spotlight icon file on iPad and iPhone (3x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Spotlight icon file on iPad and iPhone (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + + Spotlight icon file on iPad and iPhone (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + The reasons your app use active keyboard API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url]. diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index b99e825540d..d6cd2e0f3c3 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -79,33 +79,37 @@ struct IconInfo { }; static const IconInfo icon_infos[] = { - // Home screen on iPhone - { PNAME("icons/iphone_120x120"), "iphone", "Icon-120.png", "120", "2x", "60x60", false }, - { PNAME("icons/iphone_120x120"), "iphone", "Icon-120.png", "120", "3x", "40x40", false }, - { PNAME("icons/iphone_180x180"), "iphone", "Icon-180.png", "180", "3x", "60x60", false }, + // Settings on iPhone, iPad Pro, iPad, iPad mini + { PNAME("icons/settings_58x58"), "universal", "Icon-58", "58", "2x", "29x29", false }, + { PNAME("icons/settings_87x87"), "universal", "Icon-87", "87", "3x", "29x29", false }, - // Home screen on iPad - { PNAME("icons/ipad_76x76"), "ipad", "Icon-76.png", "76", "1x", "76x76", false }, - { PNAME("icons/ipad_152x152"), "ipad", "Icon-152.png", "152", "2x", "76x76", false }, - { PNAME("icons/ipad_167x167"), "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false }, + // Notifications on iPhone, iPad Pro, iPad, iPad mini + { PNAME("icons/notification_40x40"), "universal", "Icon-40", "40", "2x", "20x20", false }, + { PNAME("icons/notification_60x60"), "universal", "Icon-60", "60", "3x", "20x20", false }, + { PNAME("icons/notification_76x76"), "universal", "Icon-76", "76", "2x", "38x38", false }, + { PNAME("icons/notification_114x114"), "universal", "Icon-114", "114", "3x", "38x38", false }, + + // Spotlight on iPhone, iPad Pro, iPad, iPad mini + { PNAME("icons/spotlight_80x80"), "universal", "Icon-80", "80", "2x", "40x40", false }, + { PNAME("icons/spotlight_120x120"), "universal", "Icon-120", "120", "3x", "40x40", false }, + + // Home Screen on iPhone + { PNAME("icons/iphone_120x120"), "universal", "Icon-120-1", "120", "2x", "60x60", false }, + { PNAME("icons/iphone_180x180"), "universal", "Icon-180", "180", "3x", "60x60", false }, + + // Home Screen on iPad Pro + { PNAME("icons/ipad_167x167"), "universal", "Icon-167", "167", "2x", "83.5x83.5", false }, + + // Home Screen on iPad, iPad mini + { PNAME("icons/ipad_152x152"), "universal", "Icon-152", "152", "2x", "76x76", false }, + + { PNAME("icons/ios_128x128"), "universal", "Icon-128", "128", "2x", "64x64", false }, + { PNAME("icons/ios_192x192"), "universal", "Icon-192", "192", "3x", "64x64", false }, + + { PNAME("icons/ios_136x136"), "universal", "Icon-136", "136", "2x", "68x68", false }, // App Store - { PNAME("icons/app_store_1024x1024"), "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true }, - - // Spotlight - { PNAME("icons/spotlight_40x40"), "ipad", "Icon-40.png", "40", "1x", "40x40", false }, - { PNAME("icons/spotlight_80x80"), "iphone", "Icon-80.png", "80", "2x", "40x40", false }, - { PNAME("icons/spotlight_80x80"), "ipad", "Icon-80.png", "80", "2x", "40x40", false }, - - // Settings - { PNAME("icons/settings_58x58"), "iphone", "Icon-58.png", "58", "2x", "29x29", false }, - { PNAME("icons/settings_58x58"), "ipad", "Icon-58.png", "58", "2x", "29x29", false }, - { PNAME("icons/settings_87x87"), "iphone", "Icon-87.png", "87", "3x", "29x29", false }, - - // Notification - { PNAME("icons/notification_40x40"), "iphone", "Icon-40.png", "40", "2x", "20x20", false }, - { PNAME("icons/notification_40x40"), "ipad", "Icon-40.png", "40", "2x", "20x20", false }, - { PNAME("icons/notification_60x60"), "iphone", "Icon-60.png", "60", "3x", "20x20", false } + { PNAME("icons/app_store_1024x1024"), "universal", "Icon-1024", "1024", "1x", "1024x1024", true }, }; struct APIAccessInfo { @@ -250,7 +254,7 @@ bool EditorExportPlatformIOS::get_export_option_visibility(const EditorExportPre } bool advanced_options_enabled = p_preset->are_advanced_options_enabled(); - if (p_option.begins_with("privacy") || p_option == "application/generate_simulator_library_if_missing") { + if (p_option.begins_with("privacy") || p_option == "application/generate_simulator_library_if_missing" || (p_option.begins_with("icons/") && !p_option.begins_with("icons/icon") && !p_option.begins_with("icons/app_store"))) { return advanced_options_enabled; } @@ -368,11 +372,17 @@ void EditorExportPlatformIOS::get_export_options(List *r_options) } } + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/icon_1024x1024", PROPERTY_HINT_FILE, "*.svg,*.png,*.webp,*.jpg,*.jpeg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/icon_1024x1024_dark", PROPERTY_HINT_FILE, "*.svg,*.png,*.webp,*.jpg,*.jpeg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/icon_1024x1024_tinted", PROPERTY_HINT_FILE, "*.svg,*.png,*.webp,*.jpg,*.jpeg"), "")); + HashSet used_names; for (uint64_t i = 0; i < sizeof(icon_infos) / sizeof(icon_infos[0]); ++i) { if (!used_names.has(icon_infos[i].preset_key)) { used_names.insert(icon_infos[i].preset_key); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, icon_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, String(icon_infos[i].preset_key), PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, String(icon_infos[i].preset_key) + "_dark", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, String(icon_infos[i].preset_key) + "_tinted", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); } } r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0)); @@ -883,72 +893,127 @@ Error EditorExportPlatformIOS::_export_icons(const Ref &p_pr Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color"); + enum IconColorMode { + ICON_NORMAL, + ICON_DARK, + ICON_TINTED, + ICON_MAX, + }; + + bool first_icon = true; for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { - IconInfo info = icon_infos[i]; - int side_size = String(info.actual_size_side).to_int(); - String icon_path = p_preset->get(info.preset_key); - if (icon_path.length() == 0) { - // Resize main app icon - icon_path = GLOBAL_GET("application/config/icon"); - Ref img = memnew(Image); - Error err = ImageLoader::load_image(icon_path, img); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path)); - return ERR_UNCONFIGURED; - } else if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); - img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); - Ref new_img = Image::create_empty(side_size, side_size, false, Image::FORMAT_RGBA8); - new_img->fill(boot_bg_color); - _blend_and_rotate(new_img, img, false); - err = new_img->save_png(p_iconset_dir + info.export_name); - } else { - img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); - err = img->save_png(p_iconset_dir + info.export_name); + for (int color_mode = ICON_NORMAL; color_mode < ICON_MAX; color_mode++) { + IconInfo info = icon_infos[i]; + int side_size = String(info.actual_size_side).to_int(); + String key = info.preset_key; + String exp_name = info.export_name; + if (color_mode == ICON_DARK) { + key += "_dark"; + exp_name += "_dark"; + } else if (color_mode == ICON_TINTED) { + key += "_tinted"; + exp_name += "_tinted"; + } + exp_name += ".png"; + String icon_path = p_preset->get(key); + bool resize_waning = true; + if (icon_path.is_empty()) { + // Load and resize base icon. + key = "icons/icon_1024x1024"; + if (color_mode == ICON_DARK) { + key += "_dark"; + } else if (color_mode == ICON_TINTED) { + key += "_tinted"; + } + icon_path = p_preset->get(key); + resize_waning = false; } - if (err) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path)); - return err; + if (icon_path.is_empty()) { + if (color_mode != ICON_NORMAL) { + continue; + } + // Resize main app icon. + icon_path = GLOBAL_GET("application/config/icon"); + Ref img = memnew(Image); + Error err = ImageLoader::load_image(icon_path, img); + if (err != OK) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path)); + return ERR_UNCONFIGURED; + } else if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { + img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); + Ref new_img = Image::create_empty(side_size, side_size, false, Image::FORMAT_RGBA8); + new_img->fill(boot_bg_color); + _blend_and_rotate(new_img, img, false); + err = new_img->save_png(p_iconset_dir + exp_name); + } else { + img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); + err = img->save_png(p_iconset_dir + exp_name); + } + if (err) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path)); + return err; + } + } else { + // Load custom icon and resize if required. + Ref img = memnew(Image); + Error err = ImageLoader::load_image(icon_path, img); + if (err != OK) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path)); + return ERR_UNCONFIGURED; + } else if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { + if (resize_waning) { + add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); + } + img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); + Ref new_img = Image::create_empty(side_size, side_size, false, Image::FORMAT_RGBA8); + new_img->fill(boot_bg_color); + _blend_and_rotate(new_img, img, false); + err = new_img->save_png(p_iconset_dir + exp_name); + } else if (img->get_width() != side_size || img->get_height() != side_size) { + if (resize_waning) { + add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s): '%s' has incorrect size %s and was automatically resized to %s.", info.preset_key, icon_path, img->get_size(), Vector2i(side_size, side_size))); + } + img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); + err = img->save_png(p_iconset_dir + exp_name); + } else if (!icon_path.ends_with(".png")) { + err = img->save_png(p_iconset_dir + exp_name); + } else { + err = da->copy(icon_path, p_iconset_dir + exp_name); + } + + if (err) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path)); + return err; + } } - } else { - // Load custom icon and resize if required - Ref img = memnew(Image); - Error err = ImageLoader::load_image(icon_path, img); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path)); - return ERR_UNCONFIGURED; - } else if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); - img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); - Ref new_img = Image::create_empty(side_size, side_size, false, Image::FORMAT_RGBA8); - new_img->fill(boot_bg_color); - _blend_and_rotate(new_img, img, false); - err = new_img->save_png(p_iconset_dir + info.export_name); - } else if (img->get_width() != side_size || img->get_height() != side_size) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s): '%s' has incorrect size %s and was automatically resized to %s.", info.preset_key, icon_path, img->get_size(), Vector2i(side_size, side_size))); - img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); - err = img->save_png(p_iconset_dir + info.export_name); + sizes += String(info.actual_size_side) + "\n"; + if (first_icon) { + first_icon = false; } else { - err = da->copy(icon_path, p_iconset_dir + info.export_name); + json_description += ","; + } + json_description += String("{"); + if (color_mode != ICON_NORMAL) { + json_description += String("\"appearances\":[{"); + json_description += String("\"appearance\":\"luminosity\","); + if (color_mode == ICON_DARK) { + json_description += String("\"value\":\"dark\""); + } else if (color_mode == ICON_TINTED) { + json_description += String("\"value\":\"tinted\""); + } + json_description += String("}],"); } - - if (err) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path)); - return err; + json_description += String("\"idiom\":") + "\"" + info.idiom + "\","; + json_description += String("\"platform\":\"ios\","); + json_description += String("\"size\":") + "\"" + info.unscaled_size + "\","; + if (String(info.scale) != "1x") { + json_description += String("\"scale\":") + "\"" + info.scale + "\","; } + json_description += String("\"filename\":") + "\"" + exp_name + "\""; + json_description += String("}"); } - sizes += String(info.actual_size_side) + "\n"; - if (i > 0) { - json_description += ","; - } - json_description += String("{"); - json_description += String("\"idiom\":") + "\"" + info.idiom + "\","; - json_description += String("\"size\":") + "\"" + info.unscaled_size + "\","; - json_description += String("\"scale\":") + "\"" + info.scale + "\","; - json_description += String("\"filename\":") + "\"" + info.export_name + "\""; - json_description += String("}"); } - json_description += "]}"; + json_description += "],\"info\":{\"author\":\"xcode\",\"version\":1}}"; Ref json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE); if (json_file.is_null()) { From 975e7c8ade76c71def276ff15f46bf925cc63d23 Mon Sep 17 00:00:00 2001 From: Hendrik Brucker Date: Fri, 4 Oct 2024 13:28:15 +0200 Subject: [PATCH 21/26] [GraphEdit] Only print warning for connection layer deletion when justified --- scene/gui/graph_edit.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 9040693a6d3..646757008a1 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()) { From fa1aacb45504688fe0c0ea5a56c83db89e8ae606 Mon Sep 17 00:00:00 2001 From: DarioSamo Date: Fri, 4 Oct 2024 10:45:59 -0300 Subject: [PATCH 22/26] Configure MSAA properly in canvas renderer's pipelines. --- servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 28958f03939..979f590c4c0 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 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); From 35b3999efcf30bf5953f1fd42ab94bac3d312035 Mon Sep 17 00:00:00 2001 From: yds Date: Mon, 30 Sep 2024 00:48:08 -0300 Subject: [PATCH 23/26] Make the "Quick Open" dialog available via EditorInterface --- doc/classes/EditorInterface.xml | 8 ++++++++ editor/editor_interface.cpp | 26 ++++++++++++++++++++++++++ editor/editor_interface.h | 2 ++ 3 files changed, 36 insertions(+) diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index 795c5c1c2f0..43059db8b2e 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -343,6 +343,14 @@ [/codeblock] + + + + + + Pops up an editor dialog for quick selecting a resource file. The [param callback] must take a single argument of type [String] which will contain the path of the selected resource or be empty if the dialog is canceled. If [param base_types] is provided, the dialog will only show resources that match these types. Only types deriving from [Resource] are supported. + + diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp index fa6198f6950..264c80dcbf5 100644 --- a/editor/editor_interface.cpp +++ b/editor/editor_interface.cpp @@ -40,6 +40,7 @@ #include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" #include "editor/filesystem_dock.h" +#include "editor/gui/editor_quick_open_dialog.h" #include "editor/gui/editor_run_bar.h" #include "editor/gui/editor_scene_tabs.h" #include "editor/gui/scene_tree_editor.h" @@ -336,6 +337,24 @@ void EditorInterface::popup_property_selector(Object *p_object, const Callable & property_selector->connect(SNAME("canceled"), canceled_callback, CONNECT_DEFERRED); } +void EditorInterface::popup_quick_open(const Callable &p_callback, const TypedArray &p_base_types) { + StringName required_type = SNAME("Resource"); + Vector base_types; + if (p_base_types.is_empty()) { + base_types.append(required_type); + } else { + for (int i = 0; i < p_base_types.size(); i++) { + StringName type = p_base_types[i]; + ERR_FAIL_COND_MSG(!(ClassDB::is_parent_class(type, required_type) || EditorNode::get_editor_data().script_class_is_parent(type, required_type)), "Only types deriving from Resource are supported in the quick open dialog."); + base_types.append(type); + } + } + + EditorQuickOpenDialog *quick_open = EditorNode::get_singleton()->get_quick_open_dialog(); + quick_open->connect(SNAME("canceled"), callable_mp(this, &EditorInterface::_quick_open).bind(String(), p_callback)); + quick_open->popup_dialog(base_types, callable_mp(this, &EditorInterface::_quick_open).bind(p_callback)); +} + void EditorInterface::_node_selected(const NodePath &p_node_path, const Callable &p_callback) { const NodePath path = get_edited_scene_root()->get_path().rel_path_to(p_node_path); _call_dialog_callback(p_callback, path, "node selected"); @@ -353,6 +372,12 @@ void EditorInterface::_property_selection_canceled(const Callable &p_callback) { _call_dialog_callback(p_callback, NodePath(), "property selection canceled"); } +void EditorInterface::_quick_open(const String &p_file_path, const Callable &p_callback) { + EditorQuickOpenDialog *quick_open = EditorNode::get_singleton()->get_quick_open_dialog(); + quick_open->disconnect(SNAME("canceled"), callable_mp(this, &EditorInterface::_quick_open)); + _call_dialog_callback(p_callback, p_file_path, "quick open"); +} + void EditorInterface::_call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context) { Callable::CallError ce; Variant ret; @@ -568,6 +593,7 @@ void EditorInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("popup_node_selector", "callback", "valid_types", "current_value"), &EditorInterface::popup_node_selector, DEFVAL(TypedArray()), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("popup_property_selector", "object", "callback", "type_filter", "current_value"), &EditorInterface::popup_property_selector, DEFVAL(PackedInt32Array()), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("popup_quick_open", "callback", "base_types"), &EditorInterface::popup_quick_open, DEFVAL(TypedArray())); // Editor docks. diff --git a/editor/editor_interface.h b/editor/editor_interface.h index 20d66d71f53..4877444dac4 100644 --- a/editor/editor_interface.h +++ b/editor/editor_interface.h @@ -72,6 +72,7 @@ class EditorInterface : public Object { void _node_selection_canceled(const Callable &p_callback); void _property_selected(const String &p_property_name, const Callable &p_callback); void _property_selection_canceled(const Callable &p_callback); + void _quick_open(const String &p_file_path, const Callable &p_callback); void _call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context); // Editor tools. @@ -138,6 +139,7 @@ class EditorInterface : public Object { void popup_node_selector(const Callable &p_callback, const TypedArray &p_valid_types = TypedArray(), Node *p_current_value = nullptr); // Must use Vector because exposing Vector is not supported. void popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter = PackedInt32Array(), const String &p_current_value = String()); + void popup_quick_open(const Callable &p_callback, const TypedArray &p_base_types = TypedArray()); // Editor docks. From 794920b1ff76fcc8a01c964e2b10f5939f653190 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Fri, 4 Oct 2024 19:09:53 +0300 Subject: [PATCH 24/26] Partially revert 96780, remove warnings from project/editor settings `_get`. --- core/config/project_settings.cpp | 1 - core/io/resource_format_binary.cpp | 38 ++++++------ editor/editor_settings.cpp | 1 - scene/resources/resource_format_text.cpp | 76 +++++++++++------------- 4 files changed, 54 insertions(+), 62 deletions(-) diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 562bde978e3..9fe54e57a7a 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 3bfa0223828..109999d6125 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/editor/editor_settings.cpp b/editor/editor_settings.cpp index ceaffb64c49..ee06f08a2d7 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/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 5aa6bf718cb..e234a81c885 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()); } } } From 08af57fb8635eec8a469f7255ba3c3eb39990bb4 Mon Sep 17 00:00:00 2001 From: passivestar <60579014+passivestar@users.noreply.github.com> Date: Fri, 4 Oct 2024 22:41:57 +0400 Subject: [PATCH 25/26] Fix quick open background panel style --- editor/gui/editor_quick_open_dialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/gui/editor_quick_open_dialog.cpp b/editor/gui/editor_quick_open_dialog.cpp index 83b11e70221..94a5ff94a31 100644 --- a/editor/gui/editor_quick_open_dialog.cpp +++ b/editor/gui/editor_quick_open_dialog.cpp @@ -709,7 +709,7 @@ void QuickOpenResultContainer::_notification(int p_what) { file_details_path->add_theme_color_override(SceneStringName(font_color), text_color); no_results_label->add_theme_color_override(SceneStringName(font_color), text_color); - panel_container->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("QuickOpenBackgroundPanel"), EditorStringName(EditorStyles))); + panel_container->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree"))); if (content_display_mode == QuickOpenDisplayMode::LIST) { display_mode_toggle->set_icon(get_editor_theme_icon(SNAME("FileThumbnail"))); From 4a747bdef9c37d9f6896bec9fc336df02b0f6f3c Mon Sep 17 00:00:00 2001 From: kobewi Date: Sat, 31 Aug 2024 13:00:43 +0200 Subject: [PATCH 26/26] Add _resource_changed() helper method to EditorResourcePicker --- editor/editor_resource_picker.cpp | 30 +++++++++++++----------------- editor/editor_resource_picker.h | 1 + 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index e4ae2a62027..838cac63e7d 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 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(); - 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 old_edited_resource = edited_resource; edited_resource = Ref(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(); } } @@ -1046,8 +1043,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 parent = parent_meta[0]; diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index c39d9af764f..a596ebdc02f 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 &p_preview, const Ref &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);