diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index a632ff28f31..f3239335e1c 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -315,10 +315,10 @@ uint64_t FileAccessCompressed::get_buffer(uint8_t *p_dst, uint64_t p_length) con } else { read_block--; at_end = true; - if (i < p_length - 1) { + if (i + 1 < p_length) { read_eof = true; } - return i; + return i + 1; } } } diff --git a/doc/classes/Curve.xml b/doc/classes/Curve.xml index 3d94f8a8c1d..bfe2756cece 100644 --- a/doc/classes/Curve.xml +++ b/doc/classes/Curve.xml @@ -5,6 +5,7 @@ A curve that can be saved and re-used for other objects. By default, it ranges between [code]0[/code] and [code]1[/code] on the Y axis and positions points relative to the [code]0.5[/code] Y position. + See also [Gradient] which is designed for color interpolation. See also [Curve2D] and [Curve3D]. diff --git a/doc/classes/Gradient.xml b/doc/classes/Gradient.xml index 7cdffd14d3b..d7439f962f3 100644 --- a/doc/classes/Gradient.xml +++ b/doc/classes/Gradient.xml @@ -4,7 +4,8 @@ A color interpolator resource which can be used to generate colors between user-defined color points. - Given a set of colors, this resource will interpolate them in order. This means that if you have color 1, color 2 and color 3, the ramp will interpolate from color 1 to color 2 and from color 2 to color 3. The ramp will initially have 2 colors (black and white), one (black) at ramp lower offset 0 and the other (white) at the ramp higher offset 1. + Given a set of colors, this resource will interpolate them in order. This means that if you have color 1, color 2 and color 3, the gradient will interpolate from color 1 to color 2 and from color 2 to color 3. The gradient will initially have 2 colors (black and white), one (black) at gradient lower offset 0 and the other (white) at the gradient higher offset 1. + See also [Curve] which supports more complex easing methods, but does not support colors. diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml index 08c564aadfa..f6245e77f86 100644 --- a/doc/classes/HTTPClient.xml +++ b/doc/classes/HTTPClient.xml @@ -4,7 +4,8 @@ Low-level hyper-text transfer protocol client. - Hyper-text transfer protocol client (sometimes called "User Agent"). Used to make HTTP requests to download web content, upload files and other data or to communicate with various services, among other use cases. [b]See the [HTTPRequest] node for a higher-level alternative.[/b] + Hyper-text transfer protocol client (sometimes called "User Agent"). Used to make HTTP requests to download web content, upload files and other data or to communicate with various services, among other use cases. + See the [HTTPRequest] node for a higher-level alternative. [b]Note:[/b] This client only needs to connect to a host once (see [method connect_to_host]) to send multiple requests. Because of this, methods that take URLs usually take just the part after the host instead of the full URL, as the client is already connected to a host. See [method request] for a full example and to get started. A [HTTPClient] should be reused between multiple requests or to connect to different hosts instead of creating one client per request. Supports SSL and SSL server certificate verification. HTTP status codes in the 2xx range indicate success, 3xx redirection (i.e. "try again, but over here"), 4xx something was wrong with the request, and 5xx something went wrong on the server's side. For more information on HTTP, see https://developer.mozilla.org/en-US/docs/Web/HTTP (or read RFC 2616 to get it straight from the source: https://tools.ietf.org/html/rfc2616). diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml index c15997d0956..48c4a11c404 100644 --- a/doc/classes/MultiMesh.xml +++ b/doc/classes/MultiMesh.xml @@ -81,7 +81,7 @@ Sets the color of a specific instance by [i]multiplying[/i] the mesh's existing vertex colors. - For the color to take effect, ensure that [member color_format] is non-[code]null[/code] on the [MultiMesh] and [member SpatialMaterial.vertex_color_use_as_albedo] is [code]true[/code] on the material. + For the color to take effect, ensure that [member color_format] is non-[code]null[/code] on the [MultiMesh] and [member SpatialMaterial.vertex_color_use_as_albedo] is [code]true[/code] on the material. If the color doesn't look as expected, make sure the material's albedo color is set to pure white ([code]Color(1, 1, 1)[/code]). diff --git a/doc/classes/Shape2D.xml b/doc/classes/Shape2D.xml index edac424f7c2..cf3ec4b3d81 100644 --- a/doc/classes/Shape2D.xml +++ b/doc/classes/Shape2D.xml @@ -69,7 +69,8 @@ - The shape's custom solver bias. + The shape's custom solver bias. Defines how much bodies react to enforce contact separation when this shape is involved. + When set to [code]0.0[/code], the default value of [code]0.3[/code] is used. diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 1097b3090c2..bee606fd131 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -696,6 +696,7 @@ Splits the string by a [code]delimiter[/code] string and returns an array of the substrings, starting from right. The splits in the returned array are sorted in the same order as the original string, from left to right. + If [code]allow_empty[/code] is [code]true[/code], and there are two adjacent delimiters in the string, it will add an empty string to the array of substrings at this position. If [code]maxsplit[/code] is specified, it defines the number of splits to do from the right up to [code]maxsplit[/code]. The default value of 0 means that all items are split, thus giving the same result as [method split]. Example: [codeblock] @@ -765,6 +766,7 @@ Splits the string by a [code]delimiter[/code] string and returns an array of the substrings. The [code]delimiter[/code] can be of any length. + If [code]allow_empty[/code] is [code]true[/code], and there are two adjacent delimiters in the string, it will add an empty string to the array of substrings at this position. If [code]maxsplit[/code] is specified, it defines the number of splits to do from the left up to [code]maxsplit[/code]. The default value of [code]0[/code] means that all items are split. If you need only one element from the array at a specific index, [method get_slice] is a more performant option. Example: @@ -785,6 +787,7 @@ Splits the string in floats by using a delimiter string and returns an array of the substrings. For example, [code]"1,2.5,3"[/code] will return [code][1,2.5,3][/code] if split by [code]","[/code]. + If [code]allow_empty[/code] is [code]true[/code], and there are two adjacent delimiters in the string, it will add an empty string to the array of substrings at this position. diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h index 0373bbcc0ef..72d5050143f 100644 --- a/drivers/dummy/rasterizer_dummy.h +++ b/drivers/dummy/rasterizer_dummy.h @@ -483,6 +483,7 @@ public: Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { return Transform(); } void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) {} Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { return Transform2D(); } + uint32_t skeleton_get_revision(RID p_skeleton) const { return 0; } /* Light API */ diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 4d7a68c8301..b10e20e0367 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -3731,6 +3731,7 @@ void RasterizerStorageGLES2::skeleton_bone_set_transform_2d(RID p_skeleton, int if (!skeleton->update_list.in_list()) { skeleton_update_list.add(&skeleton->update_list); } + skeleton->revision++; } Transform2D RasterizerStorageGLES2::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { @@ -3763,6 +3764,12 @@ void RasterizerStorageGLES2::skeleton_set_base_transform_2d(RID p_skeleton, cons skeleton->base_transform_2d = p_base_transform; } +uint32_t RasterizerStorageGLES2::skeleton_get_revision(RID p_skeleton) const { + const Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, 0); + return skeleton->revision; +} + void RasterizerStorageGLES2::update_dirty_blend_shapes() { while (blend_shapes_update_list.first()) { Mesh *mesh = blend_shapes_update_list.first()->self(); diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index 4aeb91b17b4..fa9a8e6fab7 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -890,6 +890,7 @@ public: bool use_2d; int size; + uint32_t revision; // TODO use float textures for storage @@ -905,6 +906,7 @@ public: Skeleton() : use_2d(false), size(0), + revision(1), tex_id(0), update_list(this) { } @@ -924,6 +926,7 @@ public: virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform); virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const; virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform); + virtual uint32_t skeleton_get_revision(RID p_skeleton) const; void _update_skeleton_transform_buffer(const PoolVector &p_data, size_t p_size); diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 485945103be..a98f6275719 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -5277,6 +5277,8 @@ void RasterizerStorageGLES3::skeleton_bone_set_transform_2d(RID p_skeleton, int if (!skeleton->update_list.in_list()) { skeleton_update_list.add(&skeleton->update_list); } + + skeleton->revision++; } Transform2D RasterizerStorageGLES3::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); @@ -5310,6 +5312,12 @@ void RasterizerStorageGLES3::skeleton_set_base_transform_2d(RID p_skeleton, cons skeleton->base_transform_2d = p_base_transform; } +uint32_t RasterizerStorageGLES3::skeleton_get_revision(RID p_skeleton) const { + const Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, 0); + return skeleton->revision; +} + void RasterizerStorageGLES3::update_dirty_skeletons() { glActiveTexture(GL_TEXTURE0); diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index 8de3ac4e1b4..8c2a8b72e21 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -920,6 +920,7 @@ public: struct Skeleton : RID_Data { bool use_2d; int size; + uint32_t revision; Vector skel_texture; GLuint texture; SelfList update_list; @@ -929,6 +930,7 @@ public: Skeleton() : use_2d(false), size(0), + revision(1), texture(0), update_list(this) { } @@ -948,6 +950,7 @@ public: virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform); virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const; virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform); + virtual uint32_t skeleton_get_revision(RID p_skeleton) const; /* Light API */ diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 32a64f64e87..4b1eac1f393 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1448,8 +1448,7 @@ void EditorInspector::update_tree() { String group_base; VBoxContainer *category_vbox = nullptr; - List - plist; + List plist; object->get_property_list(&plist, true); HashMap item_path; diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index b0afd3fca3b..ef62f81db8b 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1450,14 +1450,13 @@ void FileSystemDock::_folder_removed(String p_folder) { void FileSystemDock::_rename_operation_confirm() { String new_name = rename_dialog_text->get_text().strip_edges(); - String old_name = tree->get_selected()->get_text(0); if (new_name.length() == 0) { EditorNode::get_singleton()->show_warning(TTR("No name provided.")); return; } else if (new_name.find("/") != -1 || new_name.find("\\") != -1 || new_name.find(":") != -1) { EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters.")); return; - } else if (to_rename.is_file && old_name.get_extension() != new_name.get_extension()) { + } else if (to_rename.is_file && to_rename.path.get_extension() != new_name.get_extension()) { if (!EditorFileSystem::get_singleton()->get_valid_extensions().find(new_name.get_extension())) { EditorNode::get_singleton()->show_warning(TTR("This file extension is not recognized by the editor.\nIf you want to rename it anyway, use your operating system's file manager.\nAfter renaming to an unknown extension, the file won't be shown in the editor anymore.")); return; diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 16539e7450d..ae28ec0baec 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -319,9 +319,9 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref &p_event) return true; } else { Vector vertices2 = _get_polygon(insert.polygon); - pre_move_edit = vertices2; edited_point = PosVertex(insert.polygon, insert.vertex + 1, xform.affine_inverse().xform(insert.pos)); vertices2.insert(edited_point.vertex, edited_point.pos); + pre_move_edit = vertices2; selected_point = edited_point; edge_point = PosVertex(); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index f113eed2d5b..e7babb072c2 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -46,6 +46,7 @@ #include "scene/2d/skeleton_2d.h" #include "scene/2d/sprite.h" #include "scene/2d/touch_screen_button.h" +#include "scene/gui/flow_container.h" #include "scene/gui/grid_container.h" #include "scene/gui/nine_patch_rect.h" #include "scene/gui/viewport_container.h" @@ -4401,7 +4402,7 @@ void CanvasItemEditor::_update_context_menu_stylebox() { context_menu_stylebox->set_border_color(accent_color); context_menu_stylebox->set_border_width(MARGIN_BOTTOM, Math::round(2 * EDSCALE)); context_menu_stylebox->set_default_margin(MARGIN_BOTTOM, 0); - context_menu_container->add_style_override("panel", context_menu_stylebox); + context_menu_panel->add_style_override("panel", context_menu_stylebox); } void CanvasItemEditor::_update_scrollbars() { @@ -5737,11 +5738,11 @@ void CanvasItemEditor::remove_control_from_info_overlay(Control *p_control) { void CanvasItemEditor::add_control_to_menu_panel(Control *p_control) { ERR_FAIL_COND(!p_control); - hbc_context_menu->add_child(p_control); + context_menu_hbox->add_child(p_control); } void CanvasItemEditor::remove_control_from_menu_panel(Control *p_control) { - hbc_context_menu->remove_child(p_control); + context_menu_hbox->remove_child(p_control); } void CanvasItemEditor::add_control_to_left_panel(Control *p_control) { @@ -5868,9 +5869,14 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { editor->call_deferred("connect", "play_pressed", this, "_update_override_camera_button", make_binds(true)); editor->call_deferred("connect", "stop_pressed", this, "_update_override_camera_button", make_binds(false)); - hb = memnew(HBoxContainer); - add_child(hb); - hb->set_anchors_and_margins_preset(Control::PRESET_WIDE); + // A fluid container for all toolbars. + HFlowContainer *main_flow = memnew(HFlowContainer); + add_child(main_flow); + + // Main toolbars. + HBoxContainer *main_menu_hbox = memnew(HBoxContainer); + main_menu_hbox->set_anchors_and_margins_preset(Control::PRESET_WIDE); + main_flow->add_child(main_menu_hbox); bottom_split = memnew(VSplitContainer); add_child(bottom_split); @@ -5968,82 +5974,82 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { updating_scroll = false; select_button = memnew(ToolButton); - hb->add_child(select_button); + main_menu_hbox->add_child(select_button); select_button->set_toggle_mode(true); select_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_SELECT)); select_button->set_pressed(true); select_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/select_mode", TTR("Select Mode"), KEY_Q)); select_button->set_tooltip(keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+Drag: Move selected node.") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("Alt+Drag: Scale selected node.") + "\n" + TTR("V: Set selected node's pivot position.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("RMB: Add node at position clicked.")); - hb->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); move_button = memnew(ToolButton); - hb->add_child(move_button); + main_menu_hbox->add_child(move_button); move_button->set_toggle_mode(true); move_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_MOVE)); move_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/move_mode", TTR("Move Mode"), KEY_W)); move_button->set_tooltip(TTR("Move Mode")); rotate_button = memnew(ToolButton); - hb->add_child(rotate_button); + main_menu_hbox->add_child(rotate_button); rotate_button->set_toggle_mode(true); rotate_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_ROTATE)); rotate_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/rotate_mode", TTR("Rotate Mode"), KEY_E)); rotate_button->set_tooltip(TTR("Rotate Mode")); scale_button = memnew(ToolButton); - hb->add_child(scale_button); + main_menu_hbox->add_child(scale_button); scale_button->set_toggle_mode(true); scale_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_SCALE)); scale_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/scale_mode", TTR("Scale Mode"), KEY_S)); scale_button->set_tooltip(TTR("Shift: Scale proportionally.")); - hb->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); list_select_button = memnew(ToolButton); - hb->add_child(list_select_button); + main_menu_hbox->add_child(list_select_button); list_select_button->set_toggle_mode(true); list_select_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_LIST_SELECT)); list_select_button->set_tooltip(TTR("Show a list of all objects at the position clicked\n(same as Alt+RMB in select mode).")); pivot_button = memnew(ToolButton); - hb->add_child(pivot_button); + main_menu_hbox->add_child(pivot_button); pivot_button->set_toggle_mode(true); pivot_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_EDIT_PIVOT)); pivot_button->set_tooltip(TTR("Click to change object's rotation pivot.")); pan_button = memnew(ToolButton); - hb->add_child(pan_button); + main_menu_hbox->add_child(pan_button); pan_button->set_toggle_mode(true); pan_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_PAN)); pan_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/pan_mode", TTR("Pan Mode"), KEY_G)); pan_button->set_tooltip(TTR("Pan Mode")); ruler_button = memnew(ToolButton); - hb->add_child(ruler_button); + main_menu_hbox->add_child(ruler_button); ruler_button->set_toggle_mode(true); ruler_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_RULER)); ruler_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/ruler_mode", TTR("Ruler Mode"), KEY_R)); ruler_button->set_tooltip(TTR("Ruler Mode")); - hb->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); smart_snap_button = memnew(ToolButton); - hb->add_child(smart_snap_button); + main_menu_hbox->add_child(smart_snap_button); smart_snap_button->set_toggle_mode(true); smart_snap_button->connect("toggled", this, "_button_toggle_smart_snap"); smart_snap_button->set_tooltip(TTR("Toggle smart snapping.")); smart_snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_smart_snap", TTR("Use Smart Snap"), KEY_MASK_SHIFT | KEY_S)); grid_snap_button = memnew(ToolButton); - hb->add_child(grid_snap_button); + main_menu_hbox->add_child(grid_snap_button); grid_snap_button->set_toggle_mode(true); grid_snap_button->connect("toggled", this, "_button_toggle_grid_snap"); grid_snap_button->set_tooltip(TTR("Toggle grid snapping.")); grid_snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_grid_snap", TTR("Use Grid Snap"), KEY_MASK_SHIFT | KEY_G)); snap_config_menu = memnew(MenuButton); - hb->add_child(snap_config_menu); + main_menu_hbox->add_child(snap_config_menu); snap_config_menu->set_h_size_flags(SIZE_SHRINK_END); snap_config_menu->set_tooltip(TTR("Snapping Options")); snap_config_menu->set_switch_on_hover(true); @@ -6072,37 +6078,37 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_other_nodes", TTR("Snap to Other Nodes")), SNAP_USE_OTHER_NODES); smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_guides", TTR("Snap to Guides")), SNAP_USE_GUIDES); - hb->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); lock_button = memnew(ToolButton); - hb->add_child(lock_button); + main_menu_hbox->add_child(lock_button); lock_button->connect("pressed", this, "_popup_callback", varray(LOCK_SELECTED)); lock_button->set_tooltip(TTR("Lock the selected object in place (can't be moved).")); lock_button->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KEY_MASK_CMD | KEY_L)); unlock_button = memnew(ToolButton); - hb->add_child(unlock_button); + main_menu_hbox->add_child(unlock_button); unlock_button->connect("pressed", this, "_popup_callback", varray(UNLOCK_SELECTED)); unlock_button->set_tooltip(TTR("Unlock the selected object (can be moved).")); unlock_button->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_L)); group_button = memnew(ToolButton); - hb->add_child(group_button); + main_menu_hbox->add_child(group_button); group_button->connect("pressed", this, "_popup_callback", varray(GROUP_SELECTED)); group_button->set_tooltip(TTR("Makes sure the object's children are not selectable.")); group_button->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KEY_MASK_CMD | KEY_G)); ungroup_button = memnew(ToolButton); - hb->add_child(ungroup_button); + main_menu_hbox->add_child(ungroup_button); ungroup_button->connect("pressed", this, "_popup_callback", varray(UNGROUP_SELECTED)); ungroup_button->set_tooltip(TTR("Restores the object's children's ability to be selected.")); ungroup_button->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_G)); - hb->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); skeleton_menu = memnew(MenuButton); - hb->add_child(skeleton_menu); + main_menu_hbox->add_child(skeleton_menu); skeleton_menu->set_tooltip(TTR("Skeleton Options")); skeleton_menu->set_switch_on_hover(true); @@ -6117,21 +6123,21 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Custom Bones")), SKELETON_CLEAR_BONES); p->connect("id_pressed", this, "_popup_callback"); - hb->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); override_camera_button = memnew(ToolButton); - hb->add_child(override_camera_button); + main_menu_hbox->add_child(override_camera_button); override_camera_button->connect("toggled", this, "_button_override_camera"); override_camera_button->set_toggle_mode(true); override_camera_button->set_disabled(true); _update_override_camera_button(false); - hb->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); view_menu = memnew(MenuButton); // TRANSLATORS: Noun, name of the 2D/3D View menus. view_menu->set_text(TTR("View")); - hb->add_child(view_menu); + main_menu_hbox->add_child(view_menu); view_menu->get_popup()->connect("id_pressed", this, "_popup_callback"); view_menu->set_switch_on_hover(true); @@ -6164,20 +6170,20 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p->add_separator(); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/preview_canvas_scale", TTR("Preview Canvas Scale"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_P), PREVIEW_CANVAS_SCALE); - hb->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); - context_menu_container = memnew(PanelContainer); - hbc_context_menu = memnew(HBoxContainer); - context_menu_container->add_child(hbc_context_menu); + context_menu_panel = memnew(PanelContainer); + context_menu_hbox = memnew(HBoxContainer); + context_menu_panel->add_child(context_menu_hbox); // Use a custom stylebox to make contextual menu items stand out from the rest. // This helps with editor usability as contextual menu items change when selecting nodes, // even though it may not be immediately obvious at first. - hb->add_child(context_menu_container); + main_flow->add_child(context_menu_panel); _update_context_menu_stylebox(); presets_menu = memnew(MenuButton); presets_menu->set_text(TTR("Layout")); - hbc_context_menu->add_child(presets_menu); + context_menu_hbox->add_child(presets_menu); presets_menu->hide(); presets_menu->set_switch_on_hover(true); @@ -6190,13 +6196,13 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { anchors_popup->connect("id_pressed", this, "_popup_callback"); anchor_mode_button = memnew(ToolButton); - hbc_context_menu->add_child(anchor_mode_button); + context_menu_hbox->add_child(anchor_mode_button); anchor_mode_button->set_toggle_mode(true); anchor_mode_button->hide(); anchor_mode_button->connect("toggled", this, "_button_toggle_anchor_mode"); animation_hb = memnew(HBoxContainer); - hbc_context_menu->add_child(animation_hb); + context_menu_hbox->add_child(animation_hb); animation_hb->add_child(memnew(VSeparator)); animation_hb->hide(); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 37d9788827a..e621b00da59 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -238,11 +238,10 @@ private: HScrollBar *h_scroll; VScrollBar *v_scroll; - HBoxContainer *hb; // Used for secondary menu items which are displayed depending on the currently selected node // (such as MeshInstance's "Mesh" menu). - PanelContainer *context_menu_container; - HBoxContainer *hbc_context_menu; + PanelContainer *context_menu_panel = nullptr; + HBoxContainer *context_menu_hbox = nullptr; ToolButton *zoom_minus; ToolButton *zoom_reset; @@ -584,8 +583,6 @@ protected: static void _bind_methods(); - HBoxContainer *get_panel_hb() { return hb; } - struct compare_items_x { bool operator()(const CanvasItem *a, const CanvasItem *b) const { return a->get_global_transform().elements[2].x < b->get_global_transform().elements[2].x; diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index ee43dcaddbf..6046006286b 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -49,6 +49,7 @@ #include "scene/3d/physics_body.h" #include "scene/3d/room_manager.h" #include "scene/3d/visual_instance.h" +#include "scene/gui/flow_container.h" #include "scene/gui/viewport_container.h" #include "scene/resources/packed_scene.h" #include "scene/resources/surface_tool.h" @@ -1171,26 +1172,18 @@ void SpatialEditorViewport::_sinput(const Ref &p_event) { float zoom_factor = 1 + (ZOOM_FREELOOK_MULTIPLIER - 1) * b->get_factor(); switch (b->get_button_index()) { case BUTTON_WHEEL_UP: { - if (b->get_alt()) { - scale_fov(-0.05); + if (is_freelook_active()) { + scale_freelook_speed(zoom_factor); } else { - if (is_freelook_active()) { - scale_freelook_speed(zoom_factor); - } else { - scale_cursor_distance(1.0 / zoom_factor); - } + scale_cursor_distance(1.0 / zoom_factor); } } break; case BUTTON_WHEEL_DOWN: { - if (b->get_alt()) { - scale_fov(0.05); + if (is_freelook_active()) { + scale_freelook_speed(1.0 / zoom_factor); } else { - if (is_freelook_active()) { - scale_freelook_speed(1.0 / zoom_factor); - } else { - scale_cursor_distance(zoom_factor); - } + scale_cursor_distance(zoom_factor); } } break; @@ -5810,7 +5803,7 @@ void SpatialEditor::_update_context_menu_stylebox() { context_menu_stylebox->set_border_color(accent_color); context_menu_stylebox->set_border_width(MARGIN_BOTTOM, Math::round(2 * EDSCALE)); context_menu_stylebox->set_default_margin(MARGIN_BOTTOM, 0); - context_menu_container->add_style_override("panel", context_menu_stylebox); + context_menu_panel->add_style_override("panel", context_menu_stylebox); } void SpatialEditor::_update_gizmos_menu() { @@ -6330,11 +6323,11 @@ void SpatialEditor::_notification(int p_what) { } void SpatialEditor::add_control_to_menu_panel(Control *p_control) { - hbc_context_menu->add_child(p_control); + context_menu_hbox->add_child(p_control); } void SpatialEditor::remove_control_from_menu_panel(Control *p_control) { - hbc_context_menu->remove_child(p_control); + context_menu_hbox->remove_child(p_control); } void SpatialEditor::set_can_preview(Camera *p_preview) { @@ -6576,15 +6569,20 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { camera_override_viewport_id = 0; - hbc_menu = memnew(HBoxContainer); - vbc->add_child(hbc_menu); + // A fluid container for all toolbars. + HFlowContainer *main_flow = memnew(HFlowContainer); + vbc->add_child(main_flow); + + // Main toolbars. + HBoxContainer *main_menu_hbox = memnew(HBoxContainer); + main_flow->add_child(main_menu_hbox); Vector button_binds; button_binds.resize(1); String sct; tool_button[TOOL_MODE_SELECT] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_MODE_SELECT]); + main_menu_hbox->add_child(tool_button[TOOL_MODE_SELECT]); tool_button[TOOL_MODE_SELECT]->set_toggle_mode(true); tool_button[TOOL_MODE_SELECT]->set_flat(true); tool_button[TOOL_MODE_SELECT]->set_pressed(true); @@ -6592,10 +6590,10 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_button[TOOL_MODE_SELECT]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_MODE_SELECT]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_select", TTR("Select Mode"), KEY_Q)); tool_button[TOOL_MODE_SELECT]->set_tooltip(keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.")); - hbc_menu->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); tool_button[TOOL_MODE_MOVE] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_MODE_MOVE]); + main_menu_hbox->add_child(tool_button[TOOL_MODE_MOVE]); tool_button[TOOL_MODE_MOVE]->set_toggle_mode(true); tool_button[TOOL_MODE_MOVE]->set_flat(true); button_binds.write[0] = MENU_TOOL_MOVE; @@ -6603,7 +6601,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_button[TOOL_MODE_MOVE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_move", TTR("Move Mode"), KEY_W)); tool_button[TOOL_MODE_ROTATE] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_MODE_ROTATE]); + main_menu_hbox->add_child(tool_button[TOOL_MODE_ROTATE]); tool_button[TOOL_MODE_ROTATE]->set_toggle_mode(true); tool_button[TOOL_MODE_ROTATE]->set_flat(true); button_binds.write[0] = MENU_TOOL_ROTATE; @@ -6611,17 +6609,17 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_button[TOOL_MODE_ROTATE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_rotate", TTR("Rotate Mode"), KEY_E)); tool_button[TOOL_MODE_SCALE] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_MODE_SCALE]); + main_menu_hbox->add_child(tool_button[TOOL_MODE_SCALE]); tool_button[TOOL_MODE_SCALE]->set_toggle_mode(true); tool_button[TOOL_MODE_SCALE]->set_flat(true); button_binds.write[0] = MENU_TOOL_SCALE; tool_button[TOOL_MODE_SCALE]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_MODE_SCALE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_scale", TTR("Scale Mode"), KEY_R)); - hbc_menu->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); tool_button[TOOL_MODE_LIST_SELECT] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_MODE_LIST_SELECT]); + main_menu_hbox->add_child(tool_button[TOOL_MODE_LIST_SELECT]); tool_button[TOOL_MODE_LIST_SELECT]->set_toggle_mode(true); tool_button[TOOL_MODE_LIST_SELECT]->set_flat(true); button_binds.write[0] = MENU_TOOL_LIST_SELECT; @@ -6629,37 +6627,37 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_button[TOOL_MODE_LIST_SELECT]->set_tooltip(TTR("Show a list of all objects at the position clicked\n(same as Alt+RMB in select mode).")); tool_button[TOOL_LOCK_SELECTED] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_LOCK_SELECTED]); + main_menu_hbox->add_child(tool_button[TOOL_LOCK_SELECTED]); button_binds.write[0] = MENU_LOCK_SELECTED; tool_button[TOOL_LOCK_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_LOCK_SELECTED]->set_tooltip(TTR("Lock the selected object in place (can't be moved).")); tool_button[TOOL_LOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KEY_MASK_CMD | KEY_L)); tool_button[TOOL_UNLOCK_SELECTED] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_UNLOCK_SELECTED]); + main_menu_hbox->add_child(tool_button[TOOL_UNLOCK_SELECTED]); button_binds.write[0] = MENU_UNLOCK_SELECTED; tool_button[TOOL_UNLOCK_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip(TTR("Unlock the selected object (can be moved).")); tool_button[TOOL_UNLOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_L)); tool_button[TOOL_GROUP_SELECTED] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_GROUP_SELECTED]); + main_menu_hbox->add_child(tool_button[TOOL_GROUP_SELECTED]); button_binds.write[0] = MENU_GROUP_SELECTED; tool_button[TOOL_GROUP_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_GROUP_SELECTED]->set_tooltip(TTR("Makes sure the object's children are not selectable.")); tool_button[TOOL_GROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KEY_MASK_CMD | KEY_G)); tool_button[TOOL_UNGROUP_SELECTED] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_UNGROUP_SELECTED]); + main_menu_hbox->add_child(tool_button[TOOL_UNGROUP_SELECTED]); button_binds.write[0] = MENU_UNGROUP_SELECTED; tool_button[TOOL_UNGROUP_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_UNGROUP_SELECTED]->set_tooltip(TTR("Restores the object's children's ability to be selected.")); tool_button[TOOL_UNGROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_G)); - hbc_menu->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); tool_option_button[TOOL_OPT_LOCAL_COORDS] = memnew(ToolButton); - hbc_menu->add_child(tool_option_button[TOOL_OPT_LOCAL_COORDS]); + main_menu_hbox->add_child(tool_option_button[TOOL_OPT_LOCAL_COORDS]); tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_toggle_mode(true); tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_flat(true); button_binds.write[0] = MENU_TOOL_LOCAL_COORDS; @@ -6667,17 +6665,17 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_shortcut(ED_SHORTCUT("spatial_editor/local_coords", TTR("Use Local Space"), KEY_T)); tool_option_button[TOOL_OPT_USE_SNAP] = memnew(ToolButton); - hbc_menu->add_child(tool_option_button[TOOL_OPT_USE_SNAP]); + main_menu_hbox->add_child(tool_option_button[TOOL_OPT_USE_SNAP]); tool_option_button[TOOL_OPT_USE_SNAP]->set_toggle_mode(true); tool_option_button[TOOL_OPT_USE_SNAP]->set_flat(true); button_binds.write[0] = MENU_TOOL_USE_SNAP; tool_option_button[TOOL_OPT_USE_SNAP]->connect("toggled", this, "_menu_item_toggled", button_binds); tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut(ED_SHORTCUT("spatial_editor/snap", TTR("Use Snap"), KEY_Y)); - hbc_menu->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); tool_option_button[TOOL_OPT_OVERRIDE_CAMERA] = memnew(ToolButton); - hbc_menu->add_child(tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]); + main_menu_hbox->add_child(tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]); tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_toggle_mode(true); tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_flat(true); tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_disabled(true); @@ -6686,7 +6684,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { _update_camera_override_button(false); tool_button[TOOL_CONVERT_ROOMS] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_CONVERT_ROOMS]); + main_menu_hbox->add_child(tool_button[TOOL_CONVERT_ROOMS]); tool_button[TOOL_CONVERT_ROOMS]->set_toggle_mode(false); tool_button[TOOL_CONVERT_ROOMS]->set_flat(true); button_binds.write[0] = MENU_TOOL_CONVERT_ROOMS; @@ -6694,7 +6692,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_button[TOOL_CONVERT_ROOMS]->set_shortcut(ED_SHORTCUT("spatial_editor/convert_rooms", TTR("Convert Rooms"), KEY_MASK_ALT | KEY_C)); tool_button[TOOL_CONVERT_ROOMS]->set_tooltip(TTR("Converts rooms for portal culling.")); - hbc_menu->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); // Drag and drop support; preview_node = memnew(Spatial); @@ -6727,7 +6725,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { transform_menu = memnew(MenuButton); transform_menu->set_text(TTR("Transform")); transform_menu->set_switch_on_hover(true); - hbc_menu->add_child(transform_menu); + main_menu_hbox->add_child(transform_menu); p = transform_menu->get_popup(); p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap Object to Floor"), KEY_PAGEDOWN), MENU_SNAP_TO_FLOOR); @@ -6742,17 +6740,17 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { // TRANSLATORS: Noun, name of the 2D/3D View menus. view_menu->set_text(TTR("View")); view_menu->set_switch_on_hover(true); - hbc_menu->add_child(view_menu); + main_menu_hbox->add_child(view_menu); - hbc_menu->add_child(memnew(VSeparator)); + main_menu_hbox->add_child(memnew(VSeparator)); - context_menu_container = memnew(PanelContainer); - hbc_context_menu = memnew(HBoxContainer); - context_menu_container->add_child(hbc_context_menu); + context_menu_panel = memnew(PanelContainer); + context_menu_hbox = memnew(HBoxContainer); + context_menu_panel->add_child(context_menu_hbox); // Use a custom stylebox to make contextual menu items stand out from the rest. // This helps with editor usability as contextual menu items change when selecting nodes, // even though it may not be immediately obvious at first. - hbc_menu->add_child(context_menu_container); + main_flow->add_child(context_menu_panel); _update_context_menu_stylebox(); // Get the view menu popup and have it stay open when a checkable item is selected diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index a867777ab59..bcb6dc80869 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -712,11 +712,10 @@ private: void _update_camera_override_button(bool p_game_running); void _update_camera_override_viewport(Object *p_viewport); - HBoxContainer *hbc_menu; // Used for secondary menu items which are displayed depending on the currently selected node // (such as MeshInstance's "Mesh" menu). - PanelContainer *context_menu_container; - HBoxContainer *hbc_context_menu; + PanelContainer *context_menu_panel = nullptr; + HBoxContainer *context_menu_hbox = nullptr; void _generate_selection_boxes(); UndoRedo *undo_redo; diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index aefc623a4d0..6d2a00780f3 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -745,9 +745,13 @@ void SpriteFramesEditor::_animation_name_edited() { undo_redo->add_undo_method(frames, "rename_animation", name, edited_anim); for (List::Element *E = nodes.front(); E; E = E->next()) { - String current = E->get()->call("get_animation"); + StringName current = E->get()->call("get_animation"); + if (current != edited_anim) { + continue; + } + undo_redo->add_do_method(E->get(), "set_animation", name); - undo_redo->add_undo_method(E->get(), "set_animation", edited_anim); + undo_redo->add_undo_method(E->get(), "set_animation", current); } undo_redo->add_do_method(this, "_update_library"); @@ -775,8 +779,14 @@ void SpriteFramesEditor::_animation_add() { undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); + // Assign the newly added animation to the edited anim sprite and to all other anim sprites having invalid animation. + Object *edited_anim_sprite = EditorNode::get_singleton()->get_inspector()->get_edited_object(); for (List::Element *E = nodes.front(); E; E = E->next()) { - String current = E->get()->call("get_animation"); + StringName current = E->get()->call("get_animation"); + if (frames->has_animation(current) && E->get() != edited_anim_sprite) { + continue; + } + undo_redo->add_do_method(E->get(), "set_animation", name); undo_redo->add_undo_method(E->get(), "set_animation", current); } @@ -811,10 +821,35 @@ void SpriteFramesEditor::_animation_remove_confirmed() { Ref frame = frames->get_frame(edited_anim, i); undo_redo->add_undo_method(frames, "add_frame", edited_anim, frame); } + + StringName new_edited_anim = StringName(); + + List anim_names; + frames->get_animation_list(&anim_names); + anim_names.sort_custom(); + + // If removing not the last animation, make the first animation left the new edited one. + if (anim_names.size() > 1) { + new_edited_anim = edited_anim != anim_names.front()->get() ? anim_names.front()->get() : anim_names.front()->next()->get(); + + List nodes; + _find_anim_sprites(EditorNode::get_singleton()->get_edited_scene(), &nodes, Ref(frames)); + + for (List::Element *E = nodes.front(); E; E = E->next()) { + StringName current = E->get()->call("get_animation"); + if (current != edited_anim) { + continue; + } + + undo_redo->add_do_method(E->get(), "set_animation", new_edited_anim); + undo_redo->add_undo_method(E->get(), "set_animation", current); + } + } + undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); - edited_anim = StringName(); + edited_anim = new_edited_anim; undo_redo->commit_action(); } diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 20ea15feba7..1c249664539 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -1315,6 +1315,10 @@ void SceneTreeDialog::_select() { } } +void SceneTreeDialog::_selected_changed() { + get_ok()->set_disabled(!tree->get_selected()); +} + void SceneTreeDialog::_filter_changed(const String &p_filter) { tree->set_filter(p_filter); } @@ -1322,6 +1326,7 @@ void SceneTreeDialog::_filter_changed(const String &p_filter) { void SceneTreeDialog::_bind_methods() { ClassDB::bind_method("_select", &SceneTreeDialog::_select); ClassDB::bind_method("_cancel", &SceneTreeDialog::_cancel); + ClassDB::bind_method(D_METHOD("_selected_changed"), &SceneTreeDialog::_selected_changed); ClassDB::bind_method(D_METHOD("_filter_changed"), &SceneTreeDialog::_filter_changed); ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::NODE_PATH, "path"))); @@ -1343,6 +1348,10 @@ SceneTreeDialog::SceneTreeDialog() { tree->set_v_size_flags(SIZE_EXPAND_FILL); tree->get_scene_tree()->connect("item_activated", this, "_select"); vbc->add_child(tree); + + // Disable the OK button when no node is selected. + get_ok()->set_disabled(!tree->get_selected()); + tree->connect("node_selected", this, "_selected_changed"); } SceneTreeDialog::~SceneTreeDialog() { diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h index 4f7ce60dc1e..ea08ba08652 100644 --- a/editor/scene_tree_editor.h +++ b/editor/scene_tree_editor.h @@ -170,6 +170,7 @@ class SceneTreeDialog : public ConfirmationDialog { void _select(); void _cancel(); + void _selected_changed(); void _filter_changed(const String &p_filter); protected: diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 4aef4baa4a4..c706c9bac78 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -425,7 +425,7 @@ - Returns an interpolation or extrapolation factor considering the range specified in [code]from[/code] and [code]to[/code], and the interpolated value specified in [code]weight[/code]. The returned value will be between [code]0.0[/code] and [code]1.0[/code] if [code]weight[/code] is between [code]from[/code] and [code]to[/code] (inclusive). If [code]weight[/code] is located outside this range, then an extrapolation factor will be returned (return value lower than [code]0.0[/code] or greater than [code]1.0[/code]). + Returns an interpolation or extrapolation factor considering the range specified in [code]from[/code] and [code]to[/code], and the interpolated value specified in [code]weight[/code]. The returned value will be between [code]0.0[/code] and [code]1.0[/code] if [code]weight[/code] is between [code]from[/code] and [code]to[/code] (inclusive). If [code]weight[/code] is located outside this range, then an extrapolation factor will be returned (return value lower than [code]0.0[/code] or greater than [code]1.0[/code]). Use [method clamp] on the result of [method inverse_lerp] if this is not desired. [codeblock] # The interpolation ratio in the `lerp()` call below is 0.75. var middle = lerp(20, 30, 0.75) @@ -434,7 +434,7 @@ var ratio = inverse_lerp(20, 30, 27.5) # `ratio` is now 0.75. [/codeblock] - See also [method lerp] which performs the reverse of this operation. + See also [method lerp] which performs the reverse of this operation, and [method range_lerp] to map a continuous series of values to another. @@ -494,14 +494,14 @@ - Linearly interpolates between two values by the factor defined in [code]weight[/code]. To perform interpolation, [code]weight[/code] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). However, values outside this range are allowed and can be used to perform [i]extrapolation[/i]. + Linearly interpolates between two values by the factor defined in [code]weight[/code]. To perform interpolation, [code]weight[/code] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). However, values outside this range are allowed and can be used to perform [i]extrapolation[/i]. Use [method clamp] on the result of [method lerp] if this is not desired. If the [code]from[/code] and [code]to[/code] arguments are of type [int] or [float], the return value is a [float]. If both are of the same vector type ([Vector2], [Vector3] or [Color]), the return value will be of the same type ([code]lerp[/code] then calls the vector type's [code]linear_interpolate[/code] method). [codeblock] lerp(0, 4, 0.75) # Returns 3.0 lerp(Vector2(1, 5), Vector2(3, 2), 0.5) # Returns Vector2(2, 3.5) [/codeblock] - See also [method inverse_lerp] which performs the reverse of this operation. To perform eased interpolation with [method lerp], combine it with [method ease] or [method smoothstep]. + See also [method inverse_lerp] which performs the reverse of this operation. To perform eased interpolation with [method lerp], combine it with [method ease] or [method smoothstep]. See also [method range_lerp] to map a continuous series of values to another. @@ -892,10 +892,11 @@ - Maps a [code]value[/code] from range [code][istart, istop][/code] to [code][ostart, ostop][/code]. + Maps a [code]value[/code] from range [code][istart, istop][/code] to [code][ostart, ostop][/code]. See also [method lerp] and [method inverse_lerp]. If [code]value[/code] is outside [code][istart, istop][/code], then the resulting value will also be outside [code][ostart, ostop][/code]. Use [method clamp] on the result of [method range_lerp] if this is not desired. [codeblock] range_lerp(75, 0, 100, -1, 1) # Returns 0.5 [/codeblock] + For complex use cases where you need multiple ranges, consider using [Curve] or [Gradient] instead. diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs index ac29efb716e..7ff1afdfbf6 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs @@ -66,6 +66,9 @@ namespace GodotTools.Ides.Rider if (string.IsNullOrEmpty(path)) return false; + if (path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) != -1) + return false; + var fileInfo = new FileInfo(path); string filename = fileInfo.Name.ToLowerInvariant(); return filename.StartsWith("rider", StringComparison.Ordinal); diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index dc1b39d0df5..5589968796c 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -147,7 +147,7 @@ void SpriteFrames::clear(const StringName &p_anim) { void SpriteFrames::clear_all() { animations.clear(); - add_animation("default"); + add_animation("default"); // Also emits changed. } void SpriteFrames::add_animation(const StringName &p_anim) { @@ -155,13 +155,16 @@ void SpriteFrames::add_animation(const StringName &p_anim) { animations[p_anim] = Anim(); animations[p_anim].normal_name = String(p_anim) + NORMAL_SUFFIX; + emit_changed(); } bool SpriteFrames::has_animation(const StringName &p_anim) const { return animations.has(p_anim); } void SpriteFrames::remove_animation(const StringName &p_anim) { - animations.erase(p_anim); + if (animations.erase(p_anim)) { + emit_changed(); + } } void SpriteFrames::rename_animation(const StringName &p_prev, const StringName &p_next) { @@ -172,17 +175,7 @@ void SpriteFrames::rename_animation(const StringName &p_prev, const StringName & animations.erase(p_prev); animations[p_next] = anim; animations[p_next].normal_name = String(p_next) + NORMAL_SUFFIX; -} - -Vector SpriteFrames::_get_animation_list() const { - Vector ret; - List al; - get_animation_list(&al); - for (List::Element *E = al.front(); E; E = E->next()) { - ret.push_back(E->get()); - } - - return ret; + emit_changed(); } void SpriteFrames::get_animation_list(List *r_animations) const { @@ -578,8 +571,12 @@ bool AnimatedSprite::is_flipped_v() const { void AnimatedSprite::_res_changed() { set_frame(frame); - _change_notify("frame"); - _change_notify("animation"); + + // Calling _change_notify("frame") and _change_notify("animation") instead wouldn't + // make EditorInspector trigger calls to _validate_property(property) which would + // lead to not updating valid values for "frame" and "animation" properties. + _change_notify(); + update(); } diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h index 81f0a3eccbc..0a69572d719 100644 --- a/scene/2d/animated_sprite.h +++ b/scene/2d/animated_sprite.h @@ -58,8 +58,6 @@ class SpriteFrames : public Resource { Array _get_animations() const; void _set_animations(const Array &p_animations); - Vector _get_animation_list() const; - protected: static void _bind_methods(); diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp index 33bf691b8c7..66b5a28d1a2 100644 --- a/scene/2d/navigation_polygon.cpp +++ b/scene/2d/navigation_polygon.cpp @@ -412,7 +412,7 @@ real_t NavigationPolygonInstance::get_enter_cost() const { void NavigationPolygonInstance::set_travel_cost(real_t p_travel_cost) { ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); travel_cost = MAX(p_travel_cost, 0.0); - Navigation2DServer::get_singleton()->region_set_enter_cost(region, travel_cost); + Navigation2DServer::get_singleton()->region_set_travel_cost(region, travel_cost); } real_t NavigationPolygonInstance::get_travel_cost() const { diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 4113ab68243..8b7f1dc6801 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -157,7 +157,11 @@ void TileMap::_update_quadrant_transform() { Transform2D nav_rel; if (bake_navigation) { - nav_rel = get_relative_transform_to_parent(navigation); + if (navigation) { + nav_rel = get_relative_transform_to_parent(navigation); + } else { + nav_rel = get_transform(); + } } for (Map::Element *E = quadrant_map.front(); E; E = E->next()) { @@ -339,7 +343,11 @@ void TileMap::update_dirty_quadrants() { Vector2 tofs = get_cell_draw_offset(); Transform2D nav_rel; if (bake_navigation) { - nav_rel = get_relative_transform_to_parent(navigation); + if (navigation) { + nav_rel = get_relative_transform_to_parent(navigation); + } else { + nav_rel = get_transform(); + } } Vector2 qofs; diff --git a/scene/3d/navigation_mesh_instance.cpp b/scene/3d/navigation_mesh_instance.cpp index 29700273336..f26aace1843 100644 --- a/scene/3d/navigation_mesh_instance.cpp +++ b/scene/3d/navigation_mesh_instance.cpp @@ -94,7 +94,7 @@ real_t NavigationMeshInstance::get_enter_cost() const { void NavigationMeshInstance::set_travel_cost(real_t p_travel_cost) { ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); travel_cost = MAX(p_travel_cost, 0.0); - NavigationServer::get_singleton()->region_set_enter_cost(region, travel_cost); + NavigationServer::get_singleton()->region_set_travel_cost(region, travel_cost); } real_t NavigationMeshInstance::get_travel_cost() const { diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 71ff30ad6d3..ce71698a77b 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -448,6 +448,7 @@ public: virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) = 0; virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0; virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0; + virtual uint32_t skeleton_get_revision(RID p_skeleton) const = 0; /* Light API */ @@ -947,19 +948,24 @@ public: }; Transform2D xform; - bool clip; - bool visible; - bool behind; - bool update_when_visible; - //VS::MaterialBlendMode blend_mode; - int light_mask; + bool clip : 1; + bool visible : 1; + bool behind : 1; + bool update_when_visible : 1; + bool distance_field : 1; + bool light_masked : 1; + mutable bool custom_rect : 1; + mutable bool rect_dirty : 1; + Vector commands; - mutable bool custom_rect; - mutable bool rect_dirty; mutable Rect2 rect; RID material; RID skeleton; + //VS::MaterialBlendMode blend_mode; + int32_t light_mask; + mutable uint32_t skeleton_revision; + Item *next; struct CopyBackBuffer { @@ -975,15 +981,29 @@ public: Item *final_clip_owner; Item *material_owner; ViewportRender *vp_render; - bool distance_field; - bool light_masked; Rect2 global_rect_cache; const Rect2 &get_rect() const { - if (custom_rect || (!rect_dirty && !update_when_visible)) { + if (custom_rect) { return rect; } + if (!rect_dirty && !update_when_visible) { + if (skeleton == RID()) { + return rect; + } else { + // special case for skeletons + uint32_t rev = RasterizerStorage::base_singleton->skeleton_get_revision(skeleton); + if (rev == skeleton_revision) { + // no change to the skeleton since we last calculated the bounding rect + return rect; + } else { + // We need to recalculate. + // Mark as done for next time. + skeleton_revision = rev; + } + } + } //must update rect int s = commands.size(); @@ -1171,6 +1191,7 @@ public: } Item() { light_mask = 1; + skeleton_revision = 0; vp_render = nullptr; next = nullptr; final_clip_owner = nullptr;