diff options
64 files changed, 448 insertions, 209 deletions
diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 8ee09ba8f8..b46719c758 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -43,7 +43,7 @@ You can access a dictionary's values by referencing the appropriate key. In the above example, [code]points_dict["White"][/code] will return [code]50[/code]. You can also write [code]points_dict.White[/code], which is equivalent. However, you'll have to use the bracket syntax if the key you're accessing the dictionary with isn't a fixed string (such as a number or variable). [codeblocks] [gdscript] - export(string, "White", "Yellow", "Orange") var my_color + export(String, "White", "Yellow", "Orange") var my_color var points_dict = {"White": 50, "Yellow": 75, "Orange": 100} func _ready(): # We can't use dot syntax here as `my_color` is a variable. diff --git a/doc/classes/Geometry2D.xml b/doc/classes/Geometry2D.xml index cbd83e8d7d..b84e221d1c 100644 --- a/doc/classes/Geometry2D.xml +++ b/doc/classes/Geometry2D.xml @@ -118,7 +118,7 @@ <argument index="2" name="from_b" type="Vector2" /> <argument index="3" name="dir_b" type="Vector2" /> <description> - Checks if the two lines ([code]from_a[/code], [code]dir_a[/code]) and ([code]from_b[/code], [code]dir_b[/code]) intersect. If yes, return the point of intersection as [Vector2]. If no intersection takes place, returns an empty [Variant]. + Checks if the two lines ([code]from_a[/code], [code]dir_a[/code]) and ([code]from_b[/code], [code]dir_b[/code]) intersect. If yes, return the point of intersection as [Vector2]. If no intersection takes place, returns [code]null[/code]. [b]Note:[/b] The lines are specified using direction vectors, not end points. </description> </method> @@ -195,7 +195,7 @@ <argument index="2" name="from_b" type="Vector2" /> <argument index="3" name="to_b" type="Vector2" /> <description> - Checks if the two segments ([code]from_a[/code], [code]to_a[/code]) and ([code]from_b[/code], [code]to_b[/code]) intersect. If yes, return the point of intersection as [Vector2]. If no intersection takes place, returns an empty [Variant]. + Checks if the two segments ([code]from_a[/code], [code]to_a[/code]) and ([code]from_b[/code], [code]to_b[/code]) intersect. If yes, return the point of intersection as [Vector2]. If no intersection takes place, returns [code]null[/code]. </description> </method> <method name="triangulate_delaunay"> diff --git a/doc/classes/Geometry3D.xml b/doc/classes/Geometry3D.xml index 0bf6f880c2..c05d7df53f 100644 --- a/doc/classes/Geometry3D.xml +++ b/doc/classes/Geometry3D.xml @@ -81,7 +81,7 @@ <argument index="3" name="b" type="Vector3" /> <argument index="4" name="c" type="Vector3" /> <description> - Tests if the 3D ray starting at [code]from[/code] with the direction of [code]dir[/code] intersects the triangle specified by [code]a[/code], [code]b[/code] and [code]c[/code]. If yes, returns the point of intersection as [Vector3]. If no intersection takes place, an empty [Variant] is returned. + Tests if the 3D ray starting at [code]from[/code] with the direction of [code]dir[/code] intersects the triangle specified by [code]a[/code], [code]b[/code] and [code]c[/code]. If yes, returns the point of intersection as [Vector3]. If no intersection takes place, returns [code]null[/code]. </description> </method> <method name="segment_intersects_convex"> @@ -121,7 +121,7 @@ <argument index="3" name="b" type="Vector3" /> <argument index="4" name="c" type="Vector3" /> <description> - Tests if the segment ([code]from[/code], [code]to[/code]) intersects the triangle [code]a[/code], [code]b[/code], [code]c[/code]. If yes, returns the point of intersection as [Vector3]. If no intersection takes place, an empty [Variant] is returned. + Tests if the segment ([code]from[/code], [code]to[/code]) intersects the triangle [code]a[/code], [code]b[/code], [code]c[/code]. If yes, returns the point of intersection as [Vector3]. If no intersection takes place, returns [code]null[/code]. </description> </method> </methods> diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index 02352ca808..965dbe7449 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -202,6 +202,9 @@ <member name="connection_lines_antialiased" type="bool" setter="set_connection_lines_antialiased" getter="is_connection_lines_antialiased" default="true"> If [code]true[/code], the lines between nodes will use antialiasing. </member> + <member name="connection_lines_curvature" type="float" setter="set_connection_lines_curvature" getter="get_connection_lines_curvature" default="0.5"> + The curvature of the lines between the nodes. 0 results in straight lines. + </member> <member name="connection_lines_thickness" type="float" setter="set_connection_lines_thickness" getter="get_connection_lines_thickness" default="2.0"> The thickness of the lines between the nodes. </member> @@ -372,15 +375,11 @@ <theme_item name="selection_stroke" data_type="color" type="Color" default="Color(1, 1, 1, 0.8)"> The outline color of the selection rectangle. </theme_item> - <theme_item name="bezier_len_neg" data_type="constant" type="int" default="160"> - </theme_item> - <theme_item name="bezier_len_pos" data_type="constant" type="int" default="80"> - </theme_item> - <theme_item name="port_grab_distance_horizontal" data_type="constant" type="int" default="24"> - The horizontal range within which a port can be grabbed (on both sides). + <theme_item name="port_hotzone_inner_extent" data_type="constant" type="int" default="22"> + The horizontal range within which a port can be grabbed (inner side). </theme_item> - <theme_item name="port_grab_distance_vertical" data_type="constant" type="int" default="26"> - The vertical range within which a port can be grabbed (on both sides). + <theme_item name="port_hotzone_outer_extent" data_type="constant" type="int" default="26"> + The horizontal range within which a port can be grabbed (outer side). </theme_item> <theme_item name="layout" data_type="icon" type="Texture2D"> </theme_item> diff --git a/doc/classes/GraphNode.xml b/doc/classes/GraphNode.xml index 5f0dca0402..f261da8413 100644 --- a/doc/classes/GraphNode.xml +++ b/doc/classes/GraphNode.xml @@ -43,6 +43,13 @@ Returns the number of enabled input slots (connections) to the GraphNode. </description> </method> + <method name="get_connection_input_height"> + <return type="int" /> + <argument index="0" name="idx" type="int" /> + <description> + Returns the height of the input connection [code]idx[/code]. + </description> + </method> <method name="get_connection_input_position"> <return type="Vector2" /> <argument index="0" name="idx" type="int" /> @@ -70,6 +77,13 @@ Returns the number of enabled output slots (connections) of the GraphNode. </description> </method> + <method name="get_connection_output_height"> + <return type="int" /> + <argument index="0" name="idx" type="int" /> + <description> + Returns the height of the output connection [code]idx[/code]. + </description> + </method> <method name="get_connection_output_position"> <return type="Vector2" /> <argument index="0" name="idx" type="int" /> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index c9795856d5..e9000d4356 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -625,7 +625,7 @@ <return type="void" /> <argument index="0" name="method" type="StringName" /> <description> - Sends a remote procedure call request for the given [code]method[/code] to peers on the network (and locally), optionally sending all additional arguments as arguments to the method called by the RPC. The call request will only be received by nodes with the same [NodePath], including the exact same node name. Behaviour depends on the RPC configuration for the given method, see [method rpc_config]. Methods are not exposed to RPCs by default. Returns an empty [Variant]. + Sends a remote procedure call request for the given [code]method[/code] to peers on the network (and locally), optionally sending all additional arguments as arguments to the method called by the RPC. The call request will only be received by nodes with the same [NodePath], including the exact same node name. Behaviour depends on the RPC configuration for the given method, see [method rpc_config]. Methods are not exposed to RPCs by default. Returns [code]null[/code]. [b]Note:[/b] You can only safely use RPCs on clients after you received the [code]connected_to_server[/code] signal from the [MultiplayerAPI]. You also need to keep track of the connection state, either by the [MultiplayerAPI] signals like [code]server_disconnected[/code] or by checking [code]get_multiplayer().peer.get_connection_status() == CONNECTION_CONNECTED[/code]. </description> </method> @@ -645,7 +645,7 @@ <argument index="0" name="peer_id" type="int" /> <argument index="1" name="method" type="StringName" /> <description> - Sends a [method rpc] to a specific peer identified by [code]peer_id[/code] (see [method MultiplayerPeer.set_target_peer]). Returns an empty [Variant]. + Sends a [method rpc] to a specific peer identified by [code]peer_id[/code] (see [method MultiplayerPeer.set_target_peer]). Returns [code]null[/code]. </description> </method> <method name="set_display_folded"> diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 6333b402da..698390a61e 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -644,6 +644,7 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() { tab_container = memnew(TabContainer); tab_container->set_use_hidden_tabs_for_min_size(true); tab_container->set_v_size_flags(Control::SIZE_EXPAND_FILL); + tab_container->set_theme_type_variation("TabContainerOdd"); tab_container->connect("tab_selected", callable_mp(this, &InputEventConfigurationDialog::_tab_selected)); main_vbox->add_child(tab_container); diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 6ed723b891..74fea03fee 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -1126,7 +1126,7 @@ ConnectionsDock::ConnectionsDock() { search_box = memnew(LineEdit); search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); - search_box->set_placeholder(TTR("Filter signals")); + search_box->set_placeholder(TTR("Filter Signals")); search_box->set_clear_button_enabled(true); search_box->connect("text_changed", callable_mp(this, &ConnectionsDock::_filter_changed)); vbc->add_child(search_box); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 60486b5286..8cb984af1b 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -1765,7 +1765,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() { search = memnew(LineEdit); search->set_h_size_flags(Control::SIZE_EXPAND_FILL); - search->set_placeholder(TTR("Filter stack variables")); + search->set_placeholder(TTR("Filter Stack Variables")); search->set_clear_button_enabled(true); tools_hb->add_child(search); diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index 88ad2633c0..6fafb2d91a 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -163,6 +163,7 @@ EditorAbout::EditorAbout() { tc->set_tab_alignment(TabBar::ALIGNMENT_CENTER); tc->set_custom_minimum_size(Size2(400, 200) * EDSCALE); tc->set_v_size_flags(Control::SIZE_EXPAND_FILL); + tc->set_theme_type_variation("TabContainerOdd"); vbc->add_child(tc); // Authors diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp index e3cbd8ad50..c1cd7f9c7b 100644 --- a/editor/editor_command_palette.cpp +++ b/editor/editor_command_palette.cpp @@ -299,7 +299,7 @@ EditorCommandPalette::EditorCommandPalette() { add_child(vbc); command_search_box = memnew(LineEdit); - command_search_box->set_placeholder(TTR("Filter commands")); + command_search_box->set_placeholder(TTR("Filter Commands")); command_search_box->connect("gui_input", callable_mp(this, &EditorCommandPalette::_sbox_input)); command_search_box->connect("text_changed", callable_mp(this, &EditorCommandPalette::_update_command_search)); command_search_box->set_v_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index dba0864fcb..19ffb6f0f5 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -73,6 +73,13 @@ void EditorHelp::_search(bool p_search_previous) { } } +void EditorHelp::_class_desc_finished() { + if (scroll_to >= 0) { + class_desc->scroll_to_paragraph(scroll_to); + } + scroll_to = -1; +} + void EditorHelp::_class_list_select(const String &p_select) { _goto_desc(p_select); } @@ -126,7 +133,11 @@ void EditorHelp::_class_desc_select(const String &p_select) { // Case order is important here to correctly handle edge cases like Variant.Type in @GlobalScope. if (table->has(link)) { // Found in the current page. - class_desc->scroll_to_paragraph((*table)[link]); + if (class_desc->is_ready()) { + class_desc->scroll_to_paragraph((*table)[link]); + } else { + scroll_to = (*table)[link]; + } } else { // Look for link in @GlobalScope. // Note that a link like @GlobalScope.enum_name will not be found in this section, only enum_name will be. @@ -1469,7 +1480,11 @@ void EditorHelp::_help_callback(const String &p_topic) { } } - class_desc->call_deferred(SNAME("scroll_to_paragraph"), line); + if (class_desc->is_ready()) { + class_desc->call_deferred(SNAME("scroll_to_paragraph"), line); + } else { + scroll_to = line; + } } static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) { @@ -1824,7 +1839,11 @@ Vector<Pair<String, int>> EditorHelp::get_sections() { void EditorHelp::scroll_to_section(int p_section_index) { _wait_for_thread(); int line = section_line[p_section_index].second; - class_desc->scroll_to_paragraph(line); + if (class_desc->is_ready()) { + class_desc->scroll_to_paragraph(line); + } else { + scroll_to = line; + } } void EditorHelp::popup_search() { @@ -1877,6 +1896,7 @@ EditorHelp::EditorHelp() { class_desc->set_v_size_flags(SIZE_EXPAND_FILL); class_desc->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4)); + class_desc->connect("finished", callable_mp(this, &EditorHelp::_class_desc_finished)); class_desc->connect("meta_clicked", callable_mp(this, &EditorHelp::_class_desc_select)); class_desc->connect("gui_input", callable_mp(this, &EditorHelp::_class_desc_input)); class_desc->connect("resized", callable_mp(this, &EditorHelp::_class_desc_resized), varray(false)); diff --git a/editor/editor_help.h b/editor/editor_help.h index b5410f6880..766a09f485 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -140,6 +140,8 @@ class EditorHelp : public VBoxContainer { Ref<Font> doc_title_font; Ref<Font> doc_code_font; + int scroll_to = -1; + void _update_theme(); void _help_callback(const String &p_topic); @@ -152,6 +154,7 @@ class EditorHelp : public VBoxContainer { void _add_bulletpoint(); + void _class_desc_finished(); void _class_list_select(const String &p_select); void _class_desc_select(const String &p_select); void _class_desc_input(const Ref<InputEvent> &p_input); diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 8d45f90ed6..dbe44aee1b 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -353,7 +353,7 @@ EditorLog::EditorLog() { // Search box search_box = memnew(LineEdit); search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); - search_box->set_placeholder(TTR("Filter messages")); + search_box->set_placeholder(TTR("Filter Messages")); search_box->set_clear_button_enabled(true); search_box->set_visible(true); search_box->connect("text_changed", callable_mp(this, &EditorLog::_search_changed)); diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp index 801a1a4641..b7073665a8 100644 --- a/editor/editor_sectioned_inspector.cpp +++ b/editor/editor_sectioned_inspector.cpp @@ -250,6 +250,11 @@ void SectionedInspector::update_category_list() { continue; } + // Filter out unnecessary ProjectSettings sections, as they already have their dedicated tabs. + if (pi.name.begins_with("autoload") || pi.name.begins_with("editor_plugins") || pi.name.begins_with("shader_globals")) { + continue; + } + if (!filter.is_empty() && !_property_path_matches(pi.name, filter, name_style)) { continue; } diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 31ce31a437..504e09aa36 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -681,6 +681,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // Visual editors EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/visual_editors/minimap_opacity", 0.85, "0.0,1.0,0.01") + EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/visual_editors/lines_curvature", 0.5, "0.0,1.0,0.01") EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "editors/visual_editors/visualshader/port_preview_size", 160, "100,400,0.01") /* Run */ diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp index 712a5b150f..3eb7d7ffbd 100644 --- a/editor/editor_settings_dialog.cpp +++ b/editor/editor_settings_dialog.cpp @@ -683,6 +683,7 @@ EditorSettingsDialog::EditorSettingsDialog() { undo_redo = memnew(UndoRedo); tabs = memnew(TabContainer); + tabs->set_theme_type_variation("TabContainerOdd"); tabs->connect("tab_changed", callable_mp(this, &EditorSettingsDialog::_tabs_tab_changed)); add_child(tabs); @@ -697,7 +698,7 @@ EditorSettingsDialog::EditorSettingsDialog() { tab_general->add_child(hbc); search_box = memnew(LineEdit); - search_box->set_placeholder(TTR("Search")); + search_box->set_placeholder(TTR("Filter Settings")); search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); hbc->add_child(search_box); @@ -739,7 +740,7 @@ EditorSettingsDialog::EditorSettingsDialog() { tab_shortcuts->set_name(TTR("Shortcuts")); shortcut_search_box = memnew(LineEdit); - shortcut_search_box->set_placeholder(TTR("Search")); + shortcut_search_box->set_placeholder(TTR("Filter Shortcuts")); shortcut_search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); tab_shortcuts->add_child(shortcut_search_box); shortcut_search_box->connect("text_changed", callable_mp(this, &EditorSettingsDialog::_filter_shortcuts)); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 550a73ed72..c53c04af4e 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1152,14 +1152,16 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_content_panel->set_border_color(dark_color_2); theme->set_stylebox("panel", "TabContainer", style_content_panel); - // These styleboxes can be used on tabs against the base color background (e.g. nested tabs). + // TabContainerOdd can be used on tabs against the base color background (e.g. nested tabs). + theme->set_type_variation("TabContainerOdd", "TabContainer"); + Ref<StyleBoxFlat> style_tab_selected_odd = style_tab_selected->duplicate(); style_tab_selected_odd->set_bg_color(disabled_bg_color); - theme->set_stylebox("tab_selected_odd", "TabContainer", style_tab_selected_odd); + theme->set_stylebox("tab_selected", "TabContainerOdd", style_tab_selected_odd); Ref<StyleBoxFlat> style_content_panel_odd = style_content_panel->duplicate(); style_content_panel_odd->set_bg_color(disabled_bg_color); - theme->set_stylebox("panel_odd", "TabContainer", style_content_panel_odd); + theme->set_stylebox("panel", "TabContainerOdd", style_content_panel_odd); // This stylebox is used in 3d and 2d viewports (no borders). Ref<StyleBoxFlat> style_content_panel_vp = style_content_panel->duplicate(); @@ -1446,8 +1448,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_icon("snap", "GraphEdit", theme->get_icon(SNAME("SnapGrid"), SNAME("EditorIcons"))); theme->set_icon("minimap", "GraphEdit", theme->get_icon(SNAME("GridMinimap"), SNAME("EditorIcons"))); theme->set_icon("layout", "GraphEdit", theme->get_icon(SNAME("GridLayout"), SNAME("EditorIcons"))); - theme->set_constant("bezier_len_pos", "GraphEdit", 80 * EDSCALE); - theme->set_constant("bezier_len_neg", "GraphEdit", 160 * EDSCALE); // GraphEditMinimap Ref<StyleBoxFlat> style_minimap_bg = make_flat_stylebox(dark_color_1, 0, 0, 0, 0); diff --git a/editor/editor_zoom_widget.cpp b/editor/editor_zoom_widget.cpp index c8099c9a0b..e4beea5e5f 100644 --- a/editor/editor_zoom_widget.cpp +++ b/editor/editor_zoom_widget.cpp @@ -178,7 +178,7 @@ EditorZoomWidget::EditorZoomWidget() { zoom_reset->add_theme_color_override("font_outline_color", Color(0, 0, 0)); zoom_reset->add_theme_color_override("font_color", Color(1, 1, 1)); zoom_reset->connect("pressed", callable_mp(this, &EditorZoomWidget::_button_zoom_reset)); - zoom_reset->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_reset", TTR("Zoom Reset"), KeyModifierMask::CMD | Key::KEY_0)); + zoom_reset->set_shortcut(ED_GET_SHORTCUT("canvas_item_editor/zoom_100_percent")); zoom_reset->set_shortcut_context(this); zoom_reset->set_focus_mode(FOCUS_NONE); zoom_reset->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index aaff892c2a..3dd0044ab9 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -3082,7 +3082,7 @@ FileSystemDock::FileSystemDock() { tree_search_box = memnew(LineEdit); tree_search_box->set_h_size_flags(SIZE_EXPAND_FILL); - tree_search_box->set_placeholder(TTR("Search files")); + tree_search_box->set_placeholder(TTR("Filter Files")); tree_search_box->connect("text_changed", callable_mp(this, &FileSystemDock::_search_changed), varray(tree_search_box)); toolbar2_hbc->add_child(tree_search_box); @@ -3127,7 +3127,7 @@ FileSystemDock::FileSystemDock() { file_list_search_box = memnew(LineEdit); file_list_search_box->set_h_size_flags(SIZE_EXPAND_FILL); - file_list_search_box->set_placeholder(TTR("Search files")); + file_list_search_box->set_placeholder(TTR("Filter Files")); file_list_search_box->connect("text_changed", callable_mp(this, &FileSystemDock::_search_changed), varray(file_list_search_box)); path_hb->add_child(file_list_search_box); diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index bbf9b11be3..f3129db65e 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -499,7 +499,7 @@ GroupDialog::GroupDialog() { add_filter = memnew(LineEdit); add_filter->set_h_size_flags(Control::SIZE_EXPAND_FILL); - add_filter->set_placeholder(TTR("Filter nodes")); + add_filter->set_placeholder(TTR("Filter Nodes")); add_filter_hbc->add_child(add_filter); add_filter->connect("text_changed", callable_mp(this, &GroupDialog::_add_filter_changed)); @@ -549,7 +549,7 @@ GroupDialog::GroupDialog() { remove_filter = memnew(LineEdit); remove_filter->set_h_size_flags(Control::SIZE_EXPAND_FILL); - remove_filter->set_placeholder(TTR("Filter nodes")); + remove_filter->set_placeholder(TTR("Filter Nodes")); remove_filter_hbc->add_child(remove_filter); remove_filter->connect("text_changed", callable_mp(this, &GroupDialog::_remove_filter_changed)); diff --git a/editor/import/dynamic_font_import_settings.cpp b/editor/import/dynamic_font_import_settings.cpp index e546f01205..97892e47d4 100644 --- a/editor/import/dynamic_font_import_settings.cpp +++ b/editor/import/dynamic_font_import_settings.cpp @@ -1418,6 +1418,7 @@ DynamicFontImportSettings::DynamicFontImportSettings() { main_pages->set_tab_alignment(TabBar::ALIGNMENT_CENTER); main_pages->set_v_size_flags(Control::SIZE_EXPAND_FILL); main_pages->set_h_size_flags(Control::SIZE_EXPAND_FILL); + main_pages->set_theme_type_variation("TabContainerOdd"); root_vb->add_child(main_pages); label_warn = memnew(Label); diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 83dff30dfa..d9ff4f79de 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -1229,6 +1229,7 @@ SceneImportSettings::SceneImportSettings() { data_mode = memnew(TabContainer); tree_split->add_child(data_mode); data_mode->set_custom_minimum_size(Size2(300 * EDSCALE, 0)); + data_mode->set_theme_type_variation("TabContainerOdd"); property_split = memnew(HSplitContainer); tree_split->add_child(property_split); diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 934d3a82b4..ad92911810 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -657,7 +657,7 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { search = memnew(LineEdit); search->set_h_size_flags(Control::SIZE_EXPAND_FILL); - search->set_placeholder(TTR("Filter properties")); + search->set_placeholder(TTR("Filter Properties")); search->set_clear_button_enabled(true); property_tools_hb->add_child(search); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 8c8505283c..5e703cf814 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -264,6 +264,8 @@ void AnimationNodeBlendTreeEditor::_update_graph() { float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); + float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"); + graph->set_connection_lines_curvature(graph_lines_curvature); } void AnimationNodeBlendTreeEditor::_file_opened(const String &p_file) { @@ -969,6 +971,8 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { graph->connect("connection_from_empty", callable_mp(this, &AnimationNodeBlendTreeEditor::_connection_from_empty)); float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); + float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"); + graph->set_connection_lines_curvature(graph_lines_curvature); VSeparator *vs = memnew(VSeparator); graph->get_zoom_hbox()->add_child(vs); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index d7061a420a..249b3a6d6a 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -1402,9 +1402,9 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { filter = memnew(LineEdit); if (templates_only) { - filter->set_placeholder(TTR("Search templates, projects, and demos")); + filter->set_placeholder(TTR("Search Templates, Projects, and Demos")); } else { - filter->set_placeholder(TTR("Search assets (excluding templates, projects, and demos)")); + filter->set_placeholder(TTR("Search Assets (Excluding Templates, Projects, and Demos)")); } filter->set_clear_button_enabled(true); search_hb->add_child(filter); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index a7e3d17fdc..d28629b41a 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -5295,14 +5295,12 @@ CanvasItemEditor::CanvasItemEditor() { // To ensure that scripts can parse the list of shortcuts correctly, we have to define // those shortcuts one by one. - // Resetting zoom to 100% is a duplicate shortcut of `canvas_item_editor/reset_zoom`, - // but it ensures both 1 and Ctrl + 0 can be used to reset zoom. ED_SHORTCUT("canvas_item_editor/zoom_3.125_percent", TTR("Zoom to 3.125%"), KeyModifierMask::SHIFT | Key::KEY_5); ED_SHORTCUT("canvas_item_editor/zoom_6.25_percent", TTR("Zoom to 6.25%"), KeyModifierMask::SHIFT | Key::KEY_4); ED_SHORTCUT("canvas_item_editor/zoom_12.5_percent", TTR("Zoom to 12.5%"), KeyModifierMask::SHIFT | Key::KEY_3); ED_SHORTCUT("canvas_item_editor/zoom_25_percent", TTR("Zoom to 25%"), KeyModifierMask::SHIFT | Key::KEY_2); ED_SHORTCUT("canvas_item_editor/zoom_50_percent", TTR("Zoom to 50%"), KeyModifierMask::SHIFT | Key::KEY_1); - ED_SHORTCUT("canvas_item_editor/zoom_100_percent", TTR("Zoom to 100%"), Key::KEY_1); + ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_100_percent", TTR("Zoom to 100%"), { (int32_t)Key::KEY_1, (int32_t)(KeyModifierMask::CMD | Key::KEY_0) }); ED_SHORTCUT("canvas_item_editor/zoom_200_percent", TTR("Zoom to 200%"), Key::KEY_2); ED_SHORTCUT("canvas_item_editor/zoom_400_percent", TTR("Zoom to 400%"), Key::KEY_3); ED_SHORTCUT("canvas_item_editor/zoom_800_percent", TTR("Zoom to 800%"), Key::KEY_4); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 99b810be44..b9d99fcc93 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3674,7 +3674,7 @@ ScriptEditor::ScriptEditor() { list_split->add_child(scripts_vbox); filter_scripts = memnew(LineEdit); - filter_scripts->set_placeholder(TTR("Filter scripts")); + filter_scripts->set_placeholder(TTR("Filter Scripts")); filter_scripts->set_clear_button_enabled(true); filter_scripts->connect("text_changed", callable_mp(this, &ScriptEditor::_filter_scripts_text_changed)); scripts_vbox->add_child(filter_scripts); @@ -3717,7 +3717,7 @@ ScriptEditor::ScriptEditor() { buttons_hbox->add_child(members_overview_alphabeta_sort_button); filter_methods = memnew(LineEdit); - filter_methods->set_placeholder(TTR("Filter methods")); + filter_methods->set_placeholder(TTR("Filter Methods")); filter_methods->set_clear_button_enabled(true); filter_methods->connect("text_changed", callable_mp(this, &ScriptEditor::_filter_methods_text_changed)); overview_vbox->add_child(filter_methods); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index a4bccf30e3..05c707c065 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1524,6 +1524,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data te->set_caret_line(row); te->set_caret_column(col); te->insert_text_at_caret(res->get_path()); + te->grab_focus(); } if (d.has("type") && (String(d["type"]) == "files" || String(d["type"]) == "files_and_dirs")) { @@ -1546,6 +1547,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data te->set_caret_line(row); te->set_caret_column(col); te->insert_text_at_caret(text_to_drop); + te->grab_focus(); } if (d.has("type") && String(d["type"]) == "nodes") { @@ -1568,9 +1570,11 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data continue; } + bool is_unique = false; String path; if (node->is_unique_name_in_owner()) { - path = "%" + node->get_name(); + path = node->get_name(); + is_unique = true; } else { path = sn->get_path_to(node); } @@ -1583,9 +1587,9 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data String variable_name = String(node->get_name()).camelcase_to_underscore(true).validate_identifier(); if (use_type) { - text_to_drop += vformat("@onready var %s: %s = $%s\n", variable_name, node->get_class_name(), path); + text_to_drop += vformat("@onready var %s: %s = %s%s\n", variable_name, node->get_class_name(), is_unique ? "%" : "$", path); } else { - text_to_drop += vformat("@onready var %s = $%s\n", variable_name, path); + text_to_drop += vformat("@onready var %s = %s%s\n", variable_name, is_unique ? "%" : "$", path); } } } else { @@ -1600,25 +1604,29 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data continue; } + bool is_unique = false; String path; if (node->is_unique_name_in_owner()) { - path = "%" + node->get_name(); + path = node->get_name(); + is_unique = true; } else { path = sn->get_path_to(node); } + for (const String &segment : path.split("/")) { if (!segment.is_valid_identifier()) { path = path.c_escape().quote(quote_style); break; } } - text_to_drop += "$" + path; + text_to_drop += (is_unique ? "%" : "$") + path; } } te->set_caret_line(row); te->set_caret_column(col); te->insert_text_at_caret(text_to_drop); + te->grab_focus(); } if (d.has("type") && String(d["type"]) == "obj_property") { @@ -1627,6 +1635,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data te->set_caret_line(row); te->set_caret_column(col); te->insert_text_at_caret(text_to_drop); + te->grab_focus(); } } diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 751751aaaa..418115c041 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -834,6 +834,8 @@ void ThemeItemImportTree::_notification(int p_what) { select_icons_warning_icon->set_texture(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons"))); select_icons_warning->add_theme_color_override("font_color", get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))); + import_items_filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); + // Bottom panel buttons. import_collapse_types_button->set_icon(get_theme_icon(SNAME("CollapseTree"), SNAME("EditorIcons"))); import_expand_types_button->set_icon(get_theme_icon(SNAME("ExpandTree"), SNAME("EditorIcons"))); @@ -881,15 +883,10 @@ void ThemeItemImportTree::_bind_methods() { } ThemeItemImportTree::ThemeItemImportTree() { - HBoxContainer *import_items_filter_hb = memnew(HBoxContainer); - add_child(import_items_filter_hb); - Label *import_items_filter_label = memnew(Label); - import_items_filter_label->set_text(TTR("Filter:")); - import_items_filter_hb->add_child(import_items_filter_label); import_items_filter = memnew(LineEdit); + import_items_filter->set_placeholder(TTR("Filter Items")); import_items_filter->set_clear_button_enabled(true); - import_items_filter->set_h_size_flags(Control::SIZE_EXPAND_FILL); - import_items_filter_hb->add_child(import_items_filter); + add_child(import_items_filter); import_items_filter->connect("text_changed", callable_mp(this, &ThemeItemImportTree::_filter_text_changed)); HBoxContainer *import_main_hb = memnew(HBoxContainer); @@ -1871,9 +1868,6 @@ void ThemeItemEditorDialog::_notification(int p_what) { edit_items_remove_all->set_icon(get_theme_icon(SNAME("ThemeRemoveAllItems"), SNAME("EditorIcons"))); import_another_theme_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); - - tc->add_theme_style_override("tab_selected", get_theme_stylebox(SNAME("tab_selected_odd"), SNAME("TabContainer"))); - tc->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel_odd"), SNAME("TabContainer"))); } break; } } @@ -1896,6 +1890,7 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito tc = memnew(TabContainer); add_child(tc); + tc->set_theme_type_variation("TabContainerOdd"); // Edit Items tab. HSplitContainer *edit_dialog_hs = memnew(HSplitContainer); @@ -3305,9 +3300,6 @@ void ThemeTypeEditor::_notification(int p_what) { data_type_tabs->set_tab_icon(5, get_theme_icon(SNAME("StyleBoxFlat"), SNAME("EditorIcons"))); data_type_tabs->set_tab_icon(6, get_theme_icon(SNAME("Tools"), SNAME("EditorIcons"))); - data_type_tabs->add_theme_style_override("tab_selected", get_theme_stylebox(SNAME("tab_selected_odd"), SNAME("TabContainer"))); - data_type_tabs->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel_odd"), SNAME("TabContainer"))); - type_variation_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); } break; } @@ -3407,6 +3399,7 @@ ThemeTypeEditor::ThemeTypeEditor() { main_vb->add_child(data_type_tabs); data_type_tabs->set_v_size_flags(SIZE_EXPAND_FILL); data_type_tabs->set_use_hidden_tabs_for_min_size(true); + data_type_tabs->set_theme_type_variation("TabContainerOdd"); color_items_list = _create_item_list(Theme::DATA_TYPE_COLOR); constant_items_list = _create_item_list(Theme::DATA_TYPE_CONSTANT); @@ -3589,7 +3582,7 @@ void ThemeEditor::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { preview_tabs->add_theme_style_override("tab_selected", get_theme_stylebox(SNAME("ThemeEditorPreviewFG"), SNAME("EditorStyles"))); preview_tabs->add_theme_style_override("tab_unselected", get_theme_stylebox(SNAME("ThemeEditorPreviewBG"), SNAME("EditorStyles"))); - preview_tabs_content->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel_odd"), SNAME("TabContainer"))); + preview_tabs_content->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainerOdd"))); add_preview_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); } break; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 6e13a31a1f..642973648e 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -1745,6 +1745,8 @@ void VisualShaderEditor::_update_graph() { float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); + float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"); + graph->set_connection_lines_curvature(graph_lines_curvature); } VisualShader::Type VisualShaderEditor::get_current_shader_type() const { @@ -4675,6 +4677,8 @@ VisualShaderEditor::VisualShaderEditor() { graph->set_drag_forwarding(this); float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); + float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"); + graph->set_connection_lines_curvature(graph_lines_curvature); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR_INT); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_BOOLEAN); diff --git a/editor/project_export.cpp b/editor/project_export.cpp index 503eb5000b..70627d5e82 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -1064,6 +1064,7 @@ ProjectExportDialog::ProjectExportDialog() { sections = memnew(TabContainer); sections->set_use_hidden_tabs_for_min_size(true); + sections->set_theme_type_variation("TabContainerOdd"); settings_vb->add_child(sections); sections->set_v_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index a56b6ec9d4..7fcabb1e80 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -2594,7 +2594,7 @@ ProjectManager::ProjectManager() { search_tree_vb->add_child(hb); search_box = memnew(LineEdit); - search_box->set_placeholder(TTR("Filter projects")); + search_box->set_placeholder(TTR("Filter Projects")); search_box->set_tooltip(TTR("This field filters projects by name and last path component.\nTo filter projects by name and full path, the query must contain at least one `/` character.")); search_box->connect("text_changed", callable_mp(this, &ProjectManager::_on_search_term_changed)); search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 14a0427e18..404199d2da 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -560,6 +560,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { tab_container = memnew(TabContainer); tab_container->set_use_hidden_tabs_for_min_size(true); + tab_container->set_theme_type_variation("TabContainerOdd"); add_child(tab_container); VBoxContainer *general_editor = memnew(VBoxContainer); @@ -586,7 +587,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { general_editor->add_child(header); property_box = memnew(LineEdit); - property_box->set_placeholder(TTR("Select a setting or type its name")); + property_box->set_placeholder(TTR("Select a Setting or Type its Name")); property_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); property_box->connect("text_changed", callable_mp(this, &ProjectSettingsEditor::_property_box_changed)); header->add_child(property_box); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index c1cc144ff5..1ddefeba77 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -3381,7 +3381,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec vbc->add_child(filter_hbc); filter = memnew(LineEdit); filter->set_h_size_flags(SIZE_EXPAND_FILL); - filter->set_placeholder(TTR("Filter nodes")); + filter->set_placeholder(TTR("Filter Nodes")); filter_hbc->add_child(filter); filter->add_theme_constant_override("minimum_character_width", 0); filter->connect("text_changed", callable_mp(this, &SceneTreeDock::_filter_changed)); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 32d43d7c59..b5b70b827b 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -1370,7 +1370,7 @@ SceneTreeDialog::SceneTreeDialog() { filter = memnew(LineEdit); filter->set_h_size_flags(Control::SIZE_EXPAND_FILL); - filter->set_placeholder(TTR("Filter nodes")); + filter->set_placeholder(TTR("Filter Nodes")); filter->set_clear_button_enabled(true); filter->add_theme_constant_override("minimum_character_width", 0); filter->connect("text_changed", callable_mp(this, &SceneTreeDialog::_filter_changed)); diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 191568661d..b86e9b386d 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -387,9 +387,9 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_member_variable = false; } - if (!in_node_path && in_region == -1 && str[j] == '$') { + if (!in_node_path && in_region == -1 && (str[j] == '$' || str[j] == '%')) { in_node_path = true; - } else if (in_region != -1 || (is_a_symbol && str[j] != '/')) { + } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%')) { in_node_path = false; } diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 478fafc930..910f94a936 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -667,20 +667,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code case GDScriptParser::Node::GET_NODE: { const GDScriptParser::GetNodeNode *get_node = static_cast<const GDScriptParser::GetNodeNode *>(p_expression); - String node_name; - if (get_node->string != nullptr) { - node_name += String(get_node->string->value); - } else { - for (int i = 0; i < get_node->chain.size(); i++) { - if (i > 0) { - node_name += "/"; - } - node_name += get_node->chain[i]->name; - } - } - Vector<GDScriptCodeGenerator::Address> args; - args.push_back(codegen.add_constant(NodePath(node_name))); + args.push_back(codegen.add_constant(NodePath(get_node->full_path))); GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype())); diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index deef593f34..cd3b7d69c5 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -270,6 +270,8 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->exit_function(); } + + _clear_stack(); #endif } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 716fcb8a7e..bc225850c9 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -2830,51 +2830,97 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre } GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) { - if (match(GDScriptTokenizer::Token::LITERAL)) { - if (previous.literal.get_type() != Variant::STRING) { - push_error(R"(Expect node path as string or identifier after "$".)"); + if (!current.is_node_name() && !check(GDScriptTokenizer::Token::LITERAL) && !check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) { + push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name())); + return nullptr; + } + + if (check(GDScriptTokenizer::Token::LITERAL)) { + if (current.literal.get_type() != Variant::STRING) { + push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name())); return nullptr; } - GetNodeNode *get_node = alloc_node<GetNodeNode>(); - make_completion_context(COMPLETION_GET_NODE, get_node); - get_node->string = parse_literal(); - return get_node; - } else if (current.is_node_name()) { - GetNodeNode *get_node = alloc_node<GetNodeNode>(); - int chain_position = 0; - do { - make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++); - if (!current.is_node_name()) { - push_error(R"(Expect node path after "/".)"); + } + + GetNodeNode *get_node = alloc_node<GetNodeNode>(); + + // Store the last item in the path so the parser knows what to expect. + // Allow allows more specific error messages. + enum PathState { + PATH_STATE_START, + PATH_STATE_SLASH, + PATH_STATE_PERCENT, + PATH_STATE_NODE_NAME, + } path_state = PATH_STATE_START; + + if (previous.type == GDScriptTokenizer::Token::DOLLAR) { + // Detect initial slash, which will be handled in the loop if it matches. + match(GDScriptTokenizer::Token::SLASH); +#ifdef DEBUG_ENABLED + } else { + get_node->use_dollar = false; +#endif + } + + int context_argument = 0; + + do { + if (previous.type == GDScriptTokenizer::Token::PERCENT) { + if (path_state != PATH_STATE_START && path_state != PATH_STATE_SLASH) { + push_error(R"("%" is only valid in the beginning of a node name (either after "$" or after "/"))"); return nullptr; } - advance(); - IdentifierNode *identifier = alloc_node<IdentifierNode>(); - identifier->name = previous.get_identifier(); - get_node->chain.push_back(identifier); - } while (match(GDScriptTokenizer::Token::SLASH)); - return get_node; - } else if (match(GDScriptTokenizer::Token::SLASH)) { - GetNodeNode *get_node = alloc_node<GetNodeNode>(); - IdentifierNode *identifier_root = alloc_node<IdentifierNode>(); - get_node->chain.push_back(identifier_root); - int chain_position = 0; - do { - make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++); - if (!current.is_node_name()) { - push_error(R"(Expect node path after "/".)"); + get_node->full_path += "%"; + + path_state = PATH_STATE_PERCENT; + } else if (previous.type == GDScriptTokenizer::Token::SLASH) { + if (path_state != PATH_STATE_START && path_state != PATH_STATE_NODE_NAME) { + push_error(R"("/" is only valid at the beginning of the path or after a node name.)"); return nullptr; } + + get_node->full_path += "/"; + + path_state = PATH_STATE_SLASH; + } + + make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++); + + if (match(GDScriptTokenizer::Token::LITERAL)) { + if (previous.literal.get_type() != Variant::STRING) { + String previous_token; + switch (path_state) { + case PATH_STATE_START: + previous_token = "$"; + break; + case PATH_STATE_PERCENT: + previous_token = "%"; + break; + case PATH_STATE_SLASH: + previous_token = "/"; + break; + default: + break; + } + push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous_token)); + return nullptr; + } + + get_node->full_path += previous.literal.operator String(); + + path_state = PATH_STATE_NODE_NAME; + } else if (current.is_node_name()) { advance(); - IdentifierNode *identifier = alloc_node<IdentifierNode>(); - identifier->name = previous.get_identifier(); - get_node->chain.push_back(identifier); - } while (match(GDScriptTokenizer::Token::SLASH)); - return get_node; - } else { - push_error(R"(Expect node path as string or identifier after "$".)"); - return nullptr; - } + get_node->full_path += previous.get_identifier(); + + path_state = PATH_STATE_NODE_NAME; + } else if (!check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) { + push_error(vformat(R"(Unexpected "%s" in node path.)", current.get_name())); + return nullptr; + } + } while (match(GDScriptTokenizer::Token::SLASH) || match(GDScriptTokenizer::Token::PERCENT)); + + return get_node; } GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_previous_operand, bool p_can_assign) { @@ -3278,7 +3324,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR, { nullptr, &GDScriptParser::parse_binary_operator, PREC_POWER }, // STAR_STAR, { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH, - { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT, + { &GDScriptParser::parse_get_node, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT, // Assignment { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // EQUAL, { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PLUS_EQUAL, @@ -4258,17 +4304,10 @@ void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function, const } void GDScriptParser::TreePrinter::print_get_node(GetNodeNode *p_get_node) { - push_text("$"); - if (p_get_node->string != nullptr) { - print_literal(p_get_node->string); - } else { - for (int i = 0; i < p_get_node->chain.size(); i++) { - if (i > 0) { - push_text("/"); - } - print_identifier(p_get_node->chain[i]); - } + if (p_get_node->use_dollar) { + push_text("$"); } + push_text(p_get_node->full_path); } void GDScriptParser::TreePrinter::print_identifier(IdentifierNode *p_identifier) { diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 96b9a10d3c..e3f8d4b8ba 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -749,8 +749,10 @@ public: }; struct GetNodeNode : public ExpressionNode { - LiteralNode *string = nullptr; - Vector<IdentifierNode *> chain; + String full_path; +#ifdef DEBUG_ENABLED + bool use_dollar = true; +#endif GetNodeNode() { type = GET_NODE; diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 8f85d8159b..55f4ebb1c5 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -3450,23 +3450,26 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time; } - // Check if this function has been interrupted by `await`. - // If that is the case we want to keep it in the debugger until it actually exits. + // Check if this is not the last time it was interrupted by `await` or if it's the first time executing. + // If that is the case then we exit the function as normal. Otherwise we postpone it until the last `await` is completed. // This ensures the call stack can be properly shown when using `await`, showing what resumed the function. - if (!awaited) { + if (!p_state || awaited) { if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->exit_function(); } - } #endif - // Clear the stack even if there was an `await`. - // The stack saved in the state is a copy, so this needs to be destructed to avoid leaks. - if (_stack_size) { - // Free stack. - for (int i = 0; i < _stack_size; i++) { + // Free stack, except reserved addresses. + for (int i = 3; i < _stack_size; i++) { stack[i].~Variant(); } +#ifdef DEBUG_ENABLED + } +#endif + + // Always free reserved addresses, since they are never copied. + for (int i = 0; i < 3; i++) { + stack[i].~Variant(); } return retvalue; diff --git a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out index b3dc181a22..9fafcb5a64 100644 --- a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out +++ b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Expect node path as string or identifier after "$". +Expected node path as string or identifier after "$". diff --git a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out index b3dc181a22..9fafcb5a64 100644 --- a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out +++ b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Expect node path as string or identifier after "$". +Expected node path as string or identifier after "$". diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out index b3dc181a22..9fafcb5a64 100644 --- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out +++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Expect node path as string or identifier after "$". +Expected node path as string or identifier after "$". diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out index dcb4ccecb0..3062f0be70 100644 --- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out +++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Expect node path after "/". +Expected node path as string or identifier after "/". diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd new file mode 100644 index 0000000000..f04f4de08d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd @@ -0,0 +1,49 @@ +extends Node + +func test(): + var child = Node.new() + child.name = "Child" + add_child(child) + child.owner = self + + var hey = Node.new() + hey.name = "Hey" + child.add_child(hey) + hey.owner = self + hey.unique_name_in_owner = true + + var fake_hey = Node.new() + fake_hey.name = "Hey" + add_child(fake_hey) + fake_hey.owner = self + + var sub_child = Node.new() + sub_child.name = "SubChild" + hey.add_child(sub_child) + sub_child.owner = self + + var howdy = Node.new() + howdy.name = "Howdy" + sub_child.add_child(howdy) + howdy.owner = self + howdy.unique_name_in_owner = true + + print(hey == $Child/Hey) + print(howdy == $Child/Hey/SubChild/Howdy) + + print(%Hey == hey) + print($%Hey == hey) + print(%"Hey" == hey) + print($"%Hey" == hey) + print($%"Hey" == hey) + print(%Hey/%Howdy == howdy) + print($%Hey/%Howdy == howdy) + print($"%Hey/%Howdy" == howdy) + print($"%Hey"/"%Howdy" == howdy) + print(%"Hey"/"%Howdy" == howdy) + print($%"Hey"/"%Howdy" == howdy) + print($"%Hey"/%"Howdy" == howdy) + print(%"Hey"/%"Howdy" == howdy) + print($%"Hey"/%"Howdy" == howdy) + print(%"Hey/%Howdy" == howdy) + print($%"Hey/%Howdy" == howdy) diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out new file mode 100644 index 0000000000..041c4439b0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out @@ -0,0 +1,19 @@ +GDTEST_OK +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index 4902adb827..cfff5c61de 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -1221,7 +1221,7 @@ GridMapEditor::GridMapEditor() { search_box = memnew(LineEdit); search_box->set_h_size_flags(SIZE_EXPAND_FILL); - search_box->set_placeholder(TTR("Filter meshes")); + search_box->set_placeholder(TTR("Filter Meshes")); hb->add_child(search_box); search_box->connect("text_changed", callable_mp(this, &GridMapEditor::_text_changed)); search_box->connect("gui_input", callable_mp(this, &GridMapEditor::_sbox_input)); diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp index 5cbaded6c1..1bc7cd7cdc 100644 --- a/modules/navigation/navigation_mesh_generator.cpp +++ b/modules/navigation/navigation_mesh_generator.cpp @@ -172,14 +172,16 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans if (Object::cast_to<MultiMeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { MultiMeshInstance3D *multimesh_instance = Object::cast_to<MultiMeshInstance3D>(p_node); Ref<MultiMesh> multimesh = multimesh_instance->get_multimesh(); - Ref<Mesh> mesh = multimesh->get_mesh(); - if (mesh.is_valid()) { - int n = multimesh->get_visible_instance_count(); - if (n == -1) { - n = multimesh->get_instance_count(); - } - for (int i = 0; i < n; i++) { - _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices); + if (multimesh.is_valid()) { + Ref<Mesh> mesh = multimesh->get_mesh(); + if (mesh.is_valid()) { + int n = multimesh->get_visible_instance_count(); + if (n == -1) { + n = multimesh->get_instance_count(); + } + for (int i = 0; i < n; i++) { + _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices); + } } } } diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp index 6e9a2e1b61..87b7f50224 100644 --- a/modules/openxr/editor/openxr_action_map_editor.cpp +++ b/modules/openxr/editor/openxr_action_map_editor.cpp @@ -344,6 +344,7 @@ OpenXRActionMapEditor::OpenXRActionMapEditor() { tabs = memnew(TabContainer); tabs->set_h_size_flags(SIZE_EXPAND_FILL); tabs->set_v_size_flags(SIZE_EXPAND_FILL); + tabs->set_theme_type_variation("TabContainerOdd"); tabs->connect("tab_changed", callable_mp(this, &OpenXRActionMapEditor::_on_tabs_tab_changed)); tabs->connect("tab_button_pressed", callable_mp(this, &OpenXRActionMapEditor::_on_tab_button_pressed)); add_child(tabs); diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp index 7454c8076f..684faf7c8e 100644 --- a/modules/visual_script/editor/visual_script_editor.cpp +++ b/modules/visual_script/editor/visual_script_editor.cpp @@ -980,6 +980,9 @@ void VisualScriptEditor::_update_graph(int p_only_id) { float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); + float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"); + graph->set_connection_lines_curvature(graph_lines_curvature); + // Use default_func instead of default_func for now I think that should be good stop gap solution to ensure not breaking anything. graph->call_deferred(SNAME("set_scroll_ofs"), script->get_scroll() * EDSCALE); updating_graph = false; @@ -4593,6 +4596,8 @@ VisualScriptEditor::VisualScriptEditor() { graph->set_drag_forwarding(this); float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); + float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"); + graph->set_connection_lines_curvature(graph_lines_curvature); graph->hide(); graph->connect("scroll_offset_changed", callable_mp(this, &VisualScriptEditor::_graph_ofs_changed)); diff --git a/platform/android/java/app/res/values/themes.xml b/platform/android/java/app/res/values/themes.xml index 99f723f5ba..d64b50ca45 100644 --- a/platform/android/java/app/res/values/themes.xml +++ b/platform/android/java/app/res/values/themes.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <style name="GodotAppMainTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen"/> + <style name="GodotAppMainTheme" parent="@android:style/Theme.Black.NoTitleBar"/> - <style name="GodotAppSplashTheme" parent="@style/GodotAppMainTheme"> + <style name="GodotAppSplashTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen"> <item name="android:windowBackground">@drawable/splash_drawable</item> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> </style> diff --git a/scene/2d/visible_on_screen_notifier_2d.cpp b/scene/2d/visible_on_screen_notifier_2d.cpp index 33dd737416..1971dc1240 100644 --- a/scene/2d/visible_on_screen_notifier_2d.cpp +++ b/scene/2d/visible_on_screen_notifier_2d.cpp @@ -66,6 +66,7 @@ void VisibleOnScreenNotifier2D::set_rect(const Rect2 &p_rect) { if (is_inside_tree()) { RS::get_singleton()->canvas_item_set_visibility_notifier(get_canvas_item(), true, rect, callable_mp(this, &VisibleOnScreenNotifier2D::_visibility_enter), callable_mp(this, &VisibleOnScreenNotifier2D::_visibility_exit)); } + update(); } Rect2 VisibleOnScreenNotifier2D::get_rect() const { diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index fdff6a88a9..446d9e800a 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -426,8 +426,8 @@ void GraphEdit::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - port_grab_distance_horizontal = get_theme_constant(SNAME("port_grab_distance_horizontal")); - port_grab_distance_vertical = get_theme_constant(SNAME("port_grab_distance_vertical")); + port_hotzone_inner_extent = get_theme_constant("port_hotzone_inner_extent"); + port_hotzone_outer_extent = get_theme_constant("port_hotzone_outer_extent"); zoom_minus->set_icon(get_theme_icon(SNAME("minus"))); zoom_reset->set_icon(get_theme_icon(SNAME("reset"))); @@ -544,8 +544,7 @@ void GraphEdit::_set_position_of_comment_enclosed_nodes(GraphNode *p_node, HashM } bool GraphEdit::_filter_input(const Point2 &p_point) { - Ref<Texture2D> port = get_theme_icon(SNAME("port"), SNAME("GraphNode")); - Vector2i port_size = Vector2i(port->get_width(), port->get_height()); + Ref<Texture2D> port_icon = get_theme_icon(SNAME("port"), SNAME("GraphNode")); for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -553,14 +552,18 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { continue; } - for (int j = 0; j < gn->get_connection_output_count(); j++) { - if (is_in_output_hotzone(gn, j, p_point / zoom, port_size)) { + for (int j = 0; j < gn->get_connection_input_count(); j++) { + Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height()); + port_size.height = MAX(port_size.height, gn->get_connection_input_height(j)); + if (is_in_input_hotzone(gn, j, p_point / zoom, port_size)) { return true; } } - for (int j = 0; j < gn->get_connection_input_count(); j++) { - if (is_in_input_hotzone(gn, j, p_point / zoom, port_size)) { + for (int j = 0; j < gn->get_connection_output_count(); j++) { + Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height()); + port_size.height = MAX(port_size.height, gn->get_connection_output_height(j)); + if (is_in_output_hotzone(gn, j, p_point / zoom, port_size)) { return true; } } @@ -572,8 +575,7 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Ref<InputEventMouseButton> mb = p_ev; if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) { - Ref<Texture2D> port = get_theme_icon(SNAME("port"), SNAME("GraphNode")); - Vector2i port_size = Vector2i(port->get_width(), port->get_height()); + Ref<Texture2D> port_icon = get_theme_icon(SNAME("port"), SNAME("GraphNode")); connecting_valid = false; click_pos = mb->get_position() / zoom; @@ -585,6 +587,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); + Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height()); + port_size.height = MAX(port_size.height, gn->get_connection_output_height(j)); + if (is_in_output_hotzone(gn, j, click_pos, port_size)) { if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) { //check disconnect @@ -629,6 +634,10 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); + + Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height()); + port_size.height = MAX(port_size.height, gn->get_connection_input_height(j)); + if (is_in_input_hotzone(gn, j, click_pos, port_size)) { if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) { //check disconnect @@ -682,11 +691,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { connecting_valid = just_disconnected || click_pos.distance_to(connecting_to / zoom) > 20.0; if (connecting_valid) { - Ref<Texture2D> port = get_theme_icon(SNAME("port"), SNAME("GraphNode")); - Vector2i port_size = Vector2i(port->get_width(), port->get_height()); - Vector2 mpos = mm->get_position() / zoom; for (int i = get_child_count() - 1; i >= 0; i--) { + Ref<Texture2D> port_icon = get_theme_icon(SNAME("port"), SNAME("GraphNode")); GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); if (!gn) { continue; @@ -695,6 +702,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { if (!connecting_out) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); + Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height()); + port_size.height = MAX(port_size.height, gn->get_connection_output_height(j)); + int type = gn->get_connection_output_type(j); if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_output_hotzone(gn, j, mpos, port_size)) { connecting_target = true; @@ -707,6 +717,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } else { for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); + Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height()); + port_size.height = MAX(port_size.height, gn->get_connection_input_height(j)); + int type = gn->get_connection_input_type(j); if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_input_hotzone(gn, j, mpos, port_size)) { connecting_target = true; @@ -754,19 +767,24 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } } -bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &pos) { - if (p_control->is_set_as_top_level() || !p_control->is_visible()) { +bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &mpos, const Vector2 &p_offset) { + if (p_control->is_set_as_top_level() || !p_control->is_visible() || !p_control->is_inside_tree()) { return false; } - if (!p_control->has_point(pos) || p_control->get_mouse_filter() == MOUSE_FILTER_IGNORE) { - //test children + Rect2 control_rect = p_control->get_rect(); + control_rect.size *= zoom; + control_rect.position *= zoom; + control_rect.position += p_offset; + + if (!control_rect.has_point(mpos) || p_control->get_mouse_filter() == MOUSE_FILTER_IGNORE) { + // Test children. for (int i = 0; i < p_control->get_child_count(); i++) { - Control *subchild = Object::cast_to<Control>(p_control->get_child(i)); - if (!subchild) { + Control *child_rect = Object::cast_to<Control>(p_control->get_child(i)); + if (!child_rect) { continue; } - if (_check_clickable_control(subchild, pos - subchild->get_position())) { + if (_check_clickable_control(child_rect, mpos, control_rect.position)) { return true; } } @@ -798,7 +816,13 @@ bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_slot_index, } bool GraphEdit::is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left) { - if (!Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2).has_point(p_mouse_pos)) { + Rect2 hotzone = Rect2( + pos.x - (p_left ? port_hotzone_outer_extent : port_hotzone_inner_extent), + pos.y - p_port_size.height / 2.0, + port_hotzone_inner_extent + port_hotzone_outer_extent, + p_port_size.height); + + if (!hotzone.has_point(p_mouse_pos)) { return false; } @@ -807,23 +831,17 @@ bool GraphEdit::is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_po if (!child) { continue; } - Rect2 rect = child->get_rect(); - - // To prevent intersections with other nodes. - rect.position *= zoom; - rect.size *= zoom; - - if (rect.has_point(p_mouse_pos)) { - //check sub-controls - Vector2 subpos = p_mouse_pos - rect.position; + Rect2 child_rect = child->get_rect(); + child_rect.size *= zoom; + if (child_rect.has_point(p_mouse_pos * zoom)) { for (int j = 0; j < child->get_child_count(); j++) { Control *subchild = Object::cast_to<Control>(child->get_child(j)); if (!subchild) { continue; } - if (_check_clickable_control(subchild, subpos - subchild->get_position())) { + if (_check_clickable_control(subchild, p_mouse_pos * zoom, child_rect.position)) { return false; } } @@ -839,13 +857,23 @@ PackedVector2Array GraphEdit::get_connection_line(const Vector2 &p_from, const V return ret; } + float x_diff = (p_to.x - p_from.x); + float cp_offset = x_diff * lines_curvature; + if (x_diff < 0) { + cp_offset *= -1; + } + Curve2D curve; - Vector<Color> colors; curve.add_point(p_from); - curve.set_point_out(0, Vector2(60, 0)); + curve.set_point_out(0, Vector2(cp_offset, 0)); curve.add_point(p_to); - curve.set_point_in(1, Vector2(-60, 0)); - return curve.tessellate(); + curve.set_point_in(1, Vector2(-cp_offset, 0)); + + if (lines_curvature > 0) { + return curve.tessellate(5, 2.0); + } else { + return curve.tessellate(1); + } } void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_zoom) { @@ -1666,6 +1694,15 @@ void GraphEdit::_minimap_toggled() { } } +void GraphEdit::set_connection_lines_curvature(float p_curvature) { + lines_curvature = p_curvature; + update(); +} + +float GraphEdit::get_connection_lines_curvature() const { + return lines_curvature; +} + void GraphEdit::set_connection_lines_thickness(float p_thickness) { lines_thickness = p_thickness; update(); @@ -2244,6 +2281,9 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_snap", "enable"), &GraphEdit::set_use_snap); ClassDB::bind_method(D_METHOD("is_using_snap"), &GraphEdit::is_using_snap); + ClassDB::bind_method(D_METHOD("set_connection_lines_curvature", "curvature"), &GraphEdit::set_connection_lines_curvature); + ClassDB::bind_method(D_METHOD("get_connection_lines_curvature"), &GraphEdit::get_connection_lines_curvature); + ClassDB::bind_method(D_METHOD("set_connection_lines_thickness", "pixels"), &GraphEdit::set_connection_lines_thickness); ClassDB::bind_method(D_METHOD("get_connection_lines_thickness"), &GraphEdit::get_connection_lines_thickness); @@ -2280,6 +2320,7 @@ void GraphEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans"), "set_panning_scheme", "get_panning_scheme"); ADD_GROUP("Connection Lines", "connection_lines"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_curvature"), "set_connection_lines_curvature", "get_connection_lines_curvature"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_thickness"), "set_connection_lines_thickness", "get_connection_lines_thickness"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "connection_lines_antialiased"), "set_connection_lines_antialiased", "is_connection_lines_antialiased"); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 5484a2317c..02e90e4717 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -124,8 +124,8 @@ private: HScrollBar *h_scroll = nullptr; VScrollBar *v_scroll = nullptr; - float port_grab_distance_horizontal = 0.0; - float port_grab_distance_vertical = 0.0; + float port_hotzone_inner_extent = 0.0; + float port_hotzone_outer_extent = 0.0; Ref<ViewPanner> panner; bool warped_panning = true; @@ -178,6 +178,7 @@ private: List<Connection> connections; float lines_thickness = 2.0f; + float lines_curvature = 0.5f; bool lines_antialiased = true; PackedVector2Array get_connection_line(const Vector2 &p_from, const Vector2 &p_to); @@ -250,7 +251,7 @@ private: friend class GraphEditMinimap; void _minimap_toggled(); - bool _check_clickable_control(Control *p_control, const Vector2 &pos); + bool _check_clickable_control(Control *p_control, const Vector2 &r_mouse_pos, const Vector2 &p_offset); bool arranging_graph = false; @@ -344,6 +345,9 @@ public: int get_snap() const; void set_snap(int p_snap); + void set_connection_lines_curvature(float p_curvature); + float get_connection_lines_curvature() const; + void set_connection_lines_thickness(float p_thickness); float get_connection_lines_thickness() const; diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 45f036d8fc..5cb28a30e8 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -832,6 +832,7 @@ void GraphNode::_connpos_update() { cc.pos = Point2i(edgeofs, y + h / 2); cc.type = slot_info[idx].type_left; cc.color = slot_info[idx].color_left; + cc.height = size.height; conn_input_cache.push_back(cc); } if (slot_info[idx].enable_right) { @@ -839,6 +840,7 @@ void GraphNode::_connpos_update() { cc.pos = Point2i(get_size().width - edgeofs, y + h / 2); cc.type = slot_info[idx].type_right; cc.color = slot_info[idx].color_right; + cc.height = size.height; conn_output_cache.push_back(cc); } } @@ -859,12 +861,13 @@ int GraphNode::get_connection_input_count() { return conn_input_cache.size(); } -int GraphNode::get_connection_output_count() { +int GraphNode::get_connection_input_height(int p_idx) { if (connpos_dirty) { _connpos_update(); } - return conn_output_cache.size(); + ERR_FAIL_INDEX_V(p_idx, conn_input_cache.size(), 0); + return conn_input_cache[p_idx].height; } Vector2 GraphNode::get_connection_input_position(int p_idx) { @@ -897,6 +900,23 @@ Color GraphNode::get_connection_input_color(int p_idx) { return conn_input_cache[p_idx].color; } +int GraphNode::get_connection_output_count() { + if (connpos_dirty) { + _connpos_update(); + } + + return conn_output_cache.size(); +} + +int GraphNode::get_connection_output_height(int p_idx) { + if (connpos_dirty) { + _connpos_update(); + } + + ERR_FAIL_INDEX_V(p_idx, conn_output_cache.size(), 0); + return conn_output_cache[p_idx].height; +} + Vector2 GraphNode::get_connection_output_position(int p_idx) { if (connpos_dirty) { _connpos_update(); @@ -1066,15 +1086,17 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_selected", "selected"), &GraphNode::set_selected); ClassDB::bind_method(D_METHOD("is_selected"), &GraphNode::is_selected); - ClassDB::bind_method(D_METHOD("get_connection_output_count"), &GraphNode::get_connection_output_count); ClassDB::bind_method(D_METHOD("get_connection_input_count"), &GraphNode::get_connection_input_count); + ClassDB::bind_method(D_METHOD("get_connection_input_height", "idx"), &GraphNode::get_connection_input_height); + ClassDB::bind_method(D_METHOD("get_connection_input_position", "idx"), &GraphNode::get_connection_input_position); + ClassDB::bind_method(D_METHOD("get_connection_input_type", "idx"), &GraphNode::get_connection_input_type); + ClassDB::bind_method(D_METHOD("get_connection_input_color", "idx"), &GraphNode::get_connection_input_color); + ClassDB::bind_method(D_METHOD("get_connection_output_count"), &GraphNode::get_connection_output_count); + ClassDB::bind_method(D_METHOD("get_connection_output_height", "idx"), &GraphNode::get_connection_output_height); ClassDB::bind_method(D_METHOD("get_connection_output_position", "idx"), &GraphNode::get_connection_output_position); ClassDB::bind_method(D_METHOD("get_connection_output_type", "idx"), &GraphNode::get_connection_output_type); ClassDB::bind_method(D_METHOD("get_connection_output_color", "idx"), &GraphNode::get_connection_output_color); - ClassDB::bind_method(D_METHOD("get_connection_input_position", "idx"), &GraphNode::get_connection_input_position); - ClassDB::bind_method(D_METHOD("get_connection_input_type", "idx"), &GraphNode::get_connection_input_type); - ClassDB::bind_method(D_METHOD("get_connection_input_color", "idx"), &GraphNode::get_connection_input_color); ClassDB::bind_method(D_METHOD("set_show_close_button", "show"), &GraphNode::set_show_close_button); ClassDB::bind_method(D_METHOD("is_close_button_visible"), &GraphNode::is_close_button_visible); diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h index 9481a7452d..f6c943dc89 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -81,6 +81,7 @@ private: Vector2 pos; int type = 0; Color color; + int height; }; Vector<ConnCache> conn_input_cache; @@ -167,10 +168,13 @@ public: bool is_close_button_visible() const; int get_connection_input_count(); - int get_connection_output_count(); + int get_connection_input_height(int p_idx); Vector2 get_connection_input_position(int p_idx); int get_connection_input_type(int p_idx); Color get_connection_input_color(int p_idx); + + int get_connection_output_count(); + int get_connection_output_height(int p_idx); Vector2 get_connection_output_position(int p_idx); int get_connection_output_type(int p_idx); Color get_connection_output_color(int p_idx); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 73188d6602..fd9db4ea1b 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -695,7 +695,7 @@ bool LineEdit::_is_over_clear_button(const Point2 &p_pos) const { return false; } Ref<Texture2D> icon = Control::get_theme_icon(SNAME("clear")); - int x_ofs = get_theme_stylebox(SNAME("normal"))->get_offset().x; + int x_ofs = get_theme_stylebox(SNAME("normal"))->get_margin(SIDE_RIGHT); return p_pos.x > get_size().width - icon->get_width() - x_ofs; } diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index b96ba0ebf9..fec8d11bef 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -862,6 +862,7 @@ void TabBar::_update_hover() { void TabBar::_update_cache() { if (tabs.is_empty()) { + buttons_visible = false; return; } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index b4602e7a5a..d3e7540790 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -2169,7 +2169,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int parent_ofs = p_pos.x + cache.item_margin; Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs; - if (c->get_first_child() != nullptr) { + if (c->get_visible_child_count() > 0) { root_pos -= Point2i(cache.arrow->get_width(), 0); } @@ -4523,6 +4523,7 @@ Point2 Tree::get_scroll() const { } void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) { + ERR_FAIL_NULL(p_item); if (!is_visible_in_tree() || !p_item->is_visible()) { return; // Hack to work around crash in get_item_rect() if Tree is not in tree. } diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index ba22fdcad3..5fcaf8f2c4 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -1004,13 +1004,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("selection_fill", "GraphEdit", Color(1, 1, 1, 0.3)); theme->set_color("selection_stroke", "GraphEdit", Color(1, 1, 1, 0.8)); theme->set_color("activity", "GraphEdit", Color(1, 1, 1)); - theme->set_constant("bezier_len_pos", "GraphEdit", 80 * scale); - theme->set_constant("bezier_len_neg", "GraphEdit", 160 * scale); // Visual Node Ports - theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 24 * scale); - theme->set_constant("port_grab_distance_vertical", "GraphEdit", 26 * scale); + theme->set_constant("port_hotzone_inner_extent", "GraphEdit", 22 * scale); + theme->set_constant("port_hotzone_outer_extent", "GraphEdit", 26 * scale); theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0)); Ref<StyleBoxFlat> style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0, 0); diff --git a/servers/physics_3d/godot_physics_server_3d.cpp b/servers/physics_3d/godot_physics_server_3d.cpp index bed9b02da1..b735283ebe 100644 --- a/servers/physics_3d/godot_physics_server_3d.cpp +++ b/servers/physics_3d/godot_physics_server_3d.cpp @@ -1671,7 +1671,7 @@ void GodotPhysicsServer3D::flush_queries() { values.push_back("flush_queries"); values.push_back(USEC_TO_SEC(OS::get_singleton()->get_ticks_usec() - time_beg)); - values.push_front("physics"); + values.push_front("physics_3d"); EngineDebugger::profiler_add_frame_data("servers", values); } #endif diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 7b92e92f8c..181d7819da 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -1391,7 +1391,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co clear_color.r *= bg_energy; clear_color.g *= bg_energy; clear_color.b *= bg_energy; - if (render_buffers_has_volumetric_fog(p_render_data->render_buffers) || environment_is_fog_enabled(p_render_data->environment)) { + if ((p_render_data->render_buffers.is_valid() && render_buffers_has_volumetric_fog(p_render_data->render_buffers)) || environment_is_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } @@ -1401,7 +1401,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co clear_color.r *= bg_energy; clear_color.g *= bg_energy; clear_color.b *= bg_energy; - if (render_buffers_has_volumetric_fog(p_render_data->render_buffers) || environment_is_fog_enabled(p_render_data->environment)) { + if ((p_render_data->render_buffers.is_valid() && render_buffers_has_volumetric_fog(p_render_data->render_buffers)) || environment_is_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } |