diff options
55 files changed, 425 insertions, 281 deletions
diff --git a/.gitignore b/.gitignore index 3946cc96e5..32a43b8c63 100644 --- a/.gitignore +++ b/.gitignore @@ -159,6 +159,7 @@ gmon.out # Kate *.kate-swp +.kateproject.build # Kdevelop *.kdev4 diff --git a/doc/classes/NativeMenu.xml b/doc/classes/NativeMenu.xml index 475874dee7..2b9e414106 100644 --- a/doc/classes/NativeMenu.xml +++ b/doc/classes/NativeMenu.xml @@ -473,6 +473,14 @@ [b]Note:[/b] This method is implemented on macOS and Windows. </description> </method> + <method name="is_opened" qualifiers="const"> + <return type="bool" /> + <param index="0" name="rid" type="RID" /> + <description> + Returns [code]true[/code] if the menu is currently opened. + [b]Note:[/b] This method is implemented only on macOS. + </description> + </method> <method name="is_system_menu" qualifiers="const"> <return type="bool" /> <param index="0" name="rid" type="RID" /> @@ -699,6 +707,7 @@ <param index="1" name="callback" type="Callable" /> <description> Registers callable to emit when the menu is about to show. + [b]Note:[/b] The OS can simulate menu opening to track menu item changes and global shortcuts, in which case the corresponding close callback is not triggered. Use [method is_opened] to check if the menu is currently opened. [b]Note:[/b] This method is implemented only on macOS. </description> </method> @@ -707,7 +716,7 @@ <param index="0" name="rid" type="RID" /> <param index="1" name="callback" type="Callable" /> <description> - Registers callable to emit when the menu is about to closed. + Registers callable to emit after the menu is closed. [b]Note:[/b] This method is implemented only on macOS. </description> </method> diff --git a/doc/classes/PhysicalSkyMaterial.xml b/doc/classes/PhysicalSkyMaterial.xml index 13f9d93e66..460134e5ba 100644 --- a/doc/classes/PhysicalSkyMaterial.xml +++ b/doc/classes/PhysicalSkyMaterial.xml @@ -6,7 +6,6 @@ <description> The [PhysicalSkyMaterial] uses the Preetham analytic daylight model to draw a sky based on physical properties. This results in a substantially more realistic sky than the [ProceduralSkyMaterial], but it is slightly slower and less flexible. The [PhysicalSkyMaterial] only supports one sun. The color, energy, and direction of the sun are taken from the first [DirectionalLight3D] in the scene tree. - As it is based on a daylight model, the sky fades to black as the sunset ends. If you want a full day/night cycle, you will have to add a night sky by converting this to a [ShaderMaterial] and adding a night sky directly into the resulting shader. </description> <tutorials> </tutorials> diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 2d497a281f..58dd659e86 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -57,6 +57,9 @@ #include "scene/main/window.h" #include "servers/audio/audio_stream.h" +constexpr double FPS_DECIMAL = 1; +constexpr double SECOND_DECIMAL = 0.0001; + void AnimationTrackKeyEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj); ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationTrackKeyEdit::_key_ofs_changed); @@ -1322,7 +1325,7 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) { return; } - p_new_len = MAX(0.0001, p_new_len); + p_new_len = MAX(SECOND_DECIMAL, p_new_len); if (use_fps && animation->get_step() > 0) { p_new_len *= animation->get_step(); } @@ -1442,7 +1445,7 @@ void AnimationTimelineEdit::_notification(int p_what) { float l = animation->get_length(); if (l <= 0) { - l = 0.0001; // Avoid crashor. + l = SECOND_DECIMAL; // Avoid crashor. } Ref<Texture2D> hsize_icon = get_editor_theme_icon(SNAME("Hsize")); @@ -1723,7 +1726,7 @@ void AnimationTimelineEdit::update_values() { editing = true; if (use_fps && animation->get_step() > 0) { length->set_value(animation->get_length() / animation->get_step()); - length->set_step(1); + length->set_step(FPS_DECIMAL); length->set_tooltip_text(TTR("Animation length (frames)")); time_icon->set_tooltip_text(TTR("Animation length (frames)")); if (track_edit) { @@ -1731,7 +1734,7 @@ void AnimationTimelineEdit::update_values() { } } else { length->set_value(animation->get_length()); - length->set_step(0.0001); + length->set_step(SECOND_DECIMAL); length->set_tooltip_text(TTR("Animation length (seconds)")); time_icon->set_tooltip_text(TTR("Animation length (seconds)")); } @@ -1912,9 +1915,9 @@ AnimationTimelineEdit::AnimationTimelineEdit() { time_icon->set_tooltip_text(TTR("Animation length (seconds)")); len_hb->add_child(time_icon); length = memnew(EditorSpinSlider); - length->set_min(0.0001); + length->set_min(SECOND_DECIMAL); length->set_max(36000); - length->set_step(0.0001); + length->set_step(SECOND_DECIMAL); length->set_allow_greater(true); length->set_custom_minimum_size(Vector2(70 * EDSCALE, 0)); length->set_hide_slider(true); @@ -2645,7 +2648,7 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { } if (key_idx != -1) { - String text = TTR("Time (s):") + " " + TS->format_number(rtos(Math::snapped(animation->track_get_key_time(track, key_idx), 0.0001))) + "\n"; + String text = TTR("Time (s):") + " " + TS->format_number(rtos(Math::snapped(animation->track_get_key_time(track, key_idx), SECOND_DECIMAL))) + "\n"; switch (animation->track_get_type(track)) { case Animation::TYPE_POSITION_3D: { Vector3 t = animation->track_get_key_value(track, key_idx); @@ -4758,10 +4761,12 @@ void AnimationTrackEditor::_animation_changed() { } void AnimationTrackEditor::_snap_mode_changed(int p_mode) { - timeline->set_use_fps(p_mode == 1); + bool use_fps = p_mode == 1; + timeline->set_use_fps(use_fps); if (key_edit) { - key_edit->set_use_fps(p_mode == 1); + key_edit->set_use_fps(use_fps); } + step->set_step(use_fps ? FPS_DECIMAL : SECOND_DECIMAL); _update_step_spinbox(); } @@ -4775,7 +4780,9 @@ void AnimationTrackEditor::_update_step_spinbox() { if (animation->get_step() == 0) { step->set_value(0); } else { - step->set_value(1.0 / animation->get_step()); + // The value stored within tscn cannot restored the original FPS due to lack of precision, + // so the value should be limited to integer. + step->set_value(Math::round(1.0 / animation->get_step())); } } else { @@ -4783,6 +4790,7 @@ void AnimationTrackEditor::_update_step_spinbox() { } step->set_block_signals(false); + _update_snap_unit(); } void AnimationTrackEditor::_animation_update() { @@ -4876,6 +4884,8 @@ void AnimationTrackEditor::_update_step(double p_new_step) { return; } + _update_snap_unit(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Change Animation Step")); float step_value = p_new_step; @@ -5162,7 +5172,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { p_ofs = snap_time(p_ofs); } while (animation->track_find_key(p_track, p_ofs, Animation::FIND_MODE_APPROX) != -1) { // Make sure insertion point is valid. - p_ofs += 0.0001; + p_ofs += SECOND_DECIMAL; } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); @@ -7007,25 +7017,31 @@ void AnimationTrackEditor::_selection_changed() { } } +void AnimationTrackEditor::_update_snap_unit() { + if (step->get_value() <= 0) { + snap_unit = 0; + return; // Avoid zero div. + } + + if (timeline->is_using_fps()) { + snap_unit = 1.0 / step->get_value(); + } else { + snap_unit = 1.0 / Math::round(1.0 / step->get_value()); // Follow the snap behavior of the timeline editor. + } +} + float AnimationTrackEditor::snap_time(float p_value, bool p_relative) { if (is_snap_enabled()) { - double snap_increment; - if (timeline->is_using_fps() && step->get_value() > 0) { - snap_increment = 1.0 / step->get_value(); - } else { - snap_increment = step->get_value(); - } - if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) { // Use more precise snapping when holding Shift. - snap_increment *= 0.25; + snap_unit *= 0.25; } if (p_relative) { - double rel = Math::fmod(timeline->get_value(), snap_increment); - p_value = Math::snapped(p_value + rel, snap_increment) - rel; + double rel = Math::fmod(timeline->get_value(), snap_unit); + p_value = Math::snapped(p_value + rel, snap_unit) - rel; } else { - p_value = Math::snapped(p_value, snap_increment); + p_value = Math::snapped(p_value, snap_unit); } } @@ -7282,7 +7298,7 @@ AnimationTrackEditor::AnimationTrackEditor() { step = memnew(EditorSpinSlider); step->set_min(0); step->set_max(1000000); - step->set_step(0.0001); + step->set_step(SECOND_DECIMAL); step->set_hide_slider(true); step->set_custom_minimum_size(Size2(100, 0) * EDSCALE); step->set_tooltip_text(TTR("Animation step value.")); @@ -7524,9 +7540,9 @@ AnimationTrackEditor::AnimationTrackEditor() { ease_selection->select(Tween::EASE_IN_OUT); // Default ease_selection->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Translation context is needed. ease_fps = memnew(SpinBox); - ease_fps->set_min(1); + ease_fps->set_min(FPS_DECIMAL); ease_fps->set_max(999); - ease_fps->set_step(1); + ease_fps->set_step(FPS_DECIMAL); ease_fps->set_value(30); // Default ease_grid->add_child(memnew(Label(TTR("Transition Type:")))); ease_grid->add_child(transition_selection); @@ -7550,9 +7566,9 @@ AnimationTrackEditor::AnimationTrackEditor() { bake_value = memnew(CheckBox); bake_value->set_pressed(true); bake_fps = memnew(SpinBox); - bake_fps->set_min(1); + bake_fps->set_min(FPS_DECIMAL); bake_fps->set_max(999); - bake_fps->set_step(1); + bake_fps->set_step(FPS_DECIMAL); bake_fps->set_value(30); // Default bake_grid->add_child(memnew(Label(TTR("3D Pos/Rot/Scl Track:")))); bake_grid->add_child(bake_trs); @@ -7671,15 +7687,14 @@ AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animat spinner->set_allow_lesser(true); if (use_fps) { - spinner->set_step(1); - spinner->set_hide_slider(true); + spinner->set_step(FPS_DECIMAL); real_t fps = animation->get_step(); if (fps > 0) { fps = 1.0 / fps; } spinner->set_value(key_ofs * fps); } else { - spinner->set_step(0.0001); + spinner->set_step(SECOND_DECIMAL); spinner->set_value(key_ofs); spinner->set_max(animation->get_length()); } diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index f449b51b81..09f751c8dd 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -649,6 +649,9 @@ class AnimationTrackEditor : public VBoxContainer { void _pick_track_select_recursive(TreeItem *p_item, const String &p_filter, Vector<Node *> &p_select_candidates); void _pick_track_filter_input(const Ref<InputEvent> &p_ie); + double snap_unit; + void _update_snap_unit(); + protected: static void _bind_methods(); void _notification(int p_what); diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 1145a10f71..cede2c0ab6 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -294,6 +294,13 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me } for (const MethodInfo &mi : p_methods) { + if (mi.name.begins_with("@")) { + // GH-92782. GDScript inline setters/getters are historically present in `get_method_list()` + // and can be called using `Object.call()`. However, these functions are meant to be internal + // and their names are not valid identifiers, so let's hide them from the user. + continue; + } + if (!p_search_string.is_empty() && !mi.name.containsn(p_search_string)) { continue; } @@ -324,8 +331,10 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me continue; } } + ret.push_back(mi); } + return ret; } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 80e2302e91..33f460e23d 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -652,6 +652,19 @@ void EditorProperty::add_focusable(Control *p_control) { focusables.push_back(p_control); } +void EditorProperty::grab_focus(int p_focusable) { + if (focusables.is_empty()) { + return; + } + + if (p_focusable >= 0) { + ERR_FAIL_INDEX(p_focusable, focusables.size()); + focusables[p_focusable]->grab_focus(); + } else { + focusables[0]->grab_focus(); + } +} + void EditorProperty::select(int p_focusable) { bool already_selected = selected; if (!selectable) { diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index a0ced55bd8..f9b0d1f094 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -192,6 +192,7 @@ public: void set_deletable(bool p_enable); bool is_deletable() const; void add_focusable(Control *p_control); + void grab_focus(int p_focusable = -1); void select(int p_focusable = -1); void deselect(); bool is_selected() const; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index ebdad467ff..45d276dd79 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3706,12 +3706,15 @@ void EditorNode::_remove_scene(int index, bool p_change_tab) { } void EditorNode::set_edited_scene(Node *p_scene) { + set_edited_scene_root(p_scene, true); +} + +void EditorNode::set_edited_scene_root(Node *p_scene, bool p_auto_add) { Node *old_edited_scene_root = get_editor_data().get_edited_scene_root(); - if (old_edited_scene_root) { - if (old_edited_scene_root->get_parent() == scene_root) { - scene_root->remove_child(old_edited_scene_root); - } - old_edited_scene_root->disconnect(SNAME("replacing_by"), callable_mp(this, &EditorNode::set_edited_scene)); + ERR_FAIL_COND_MSG(p_scene && p_scene != old_edited_scene_root && p_scene->get_parent(), "Non-null nodes that are set as edited scene should not have a parent node."); + + if (p_auto_add && old_edited_scene_root && old_edited_scene_root->get_parent() == scene_root) { + scene_root->remove_child(old_edited_scene_root); } get_editor_data().set_edited_scene_root(p_scene); @@ -3723,11 +3726,8 @@ void EditorNode::set_edited_scene(Node *p_scene) { get_tree()->set_edited_scene_root(p_scene); } - if (p_scene) { - if (p_scene->get_parent() != scene_root) { - scene_root->add_child(p_scene, true); - } - p_scene->connect(SNAME("replacing_by"), callable_mp(this, &EditorNode::set_edited_scene)); + if (p_auto_add && p_scene) { + scene_root->add_child(p_scene, true); } } @@ -5967,6 +5967,8 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins instantiated_node->set_scene_file_path(String()); } current_edited_scene = instantiated_node; + + editor_data.set_edited_scene_root(current_edited_scene); } // Replace the original node with the instantiated version. diff --git a/editor/editor_node.h b/editor/editor_node.h index 5d7bd5b4f8..899da99450 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -783,6 +783,7 @@ public: SubViewport *get_scene_root() { return scene_root; } // Root of the scene being edited. void set_edited_scene(Node *p_scene); + void set_edited_scene_root(Node *p_scene, bool p_auto_add); Node *get_edited_scene() { return editor_data.get_edited_scene_root(); } void fix_dependencies(const String &p_for_file); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 555165c156..53a10a779f 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -255,6 +255,10 @@ void EditorPropertyArray::_change_type_menu(int p_index) { return; } + ERR_FAIL_COND_MSG( + changing_type_index == EditorPropertyArrayObject::NOT_CHANGING_TYPE, + "Tried to change type of an array item, but no item was selected."); + Variant value; VariantInternal::initialize(&value, Variant::Type(p_index)); @@ -444,6 +448,10 @@ void EditorPropertyArray::update_property() { slot.prop = new_prop; slot.set_index(idx); } + if (slot.index == changing_type_index) { + callable_mp(slot.prop, &EditorProperty::grab_focus).call_deferred(0); + changing_type_index = EditorPropertyArrayObject::NOT_CHANGING_TYPE; + } slot.prop->update_property(); } @@ -921,6 +929,10 @@ void EditorPropertyDictionary::_create_new_property_slot(int p_idx) { } void EditorPropertyDictionary::_change_type_menu(int p_index) { + ERR_FAIL_COND_MSG( + changing_type_index == EditorPropertyDictionaryObject::NOT_CHANGING_TYPE, + "Tried to change the type of a dict key or value, but nothing was selected."); + Variant value; switch (changing_type_index) { case EditorPropertyDictionaryObject::NEW_KEY_INDEX: @@ -1062,6 +1074,14 @@ void EditorPropertyDictionary::update_property() { new_prop->set_read_only(is_read_only()); slot.set_prop(new_prop); } + + // We need to grab the focus of the property that is being changed, even if the type didn't actually changed. + // Otherwise, focus will stay on the change type button, which is not very user friendly. + if (changing_type_index == slot.index) { + callable_mp(slot.prop, &EditorProperty::grab_focus).call_deferred(0); + changing_type_index = EditorPropertyDictionaryObject::NOT_CHANGING_TYPE; // Reset to avoid grabbing focus again. + } + slot.prop->update_property(); } updating = false; diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h index 8b939ab0b0..267cb1e86d 100644 --- a/editor/editor_properties_array_dict.h +++ b/editor/editor_properties_array_dict.h @@ -49,6 +49,10 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; public: + enum { + NOT_CHANGING_TYPE = -1, + }; + void set_array(const Variant &p_array); Variant get_array(); @@ -68,7 +72,8 @@ protected: public: enum { - NEW_KEY_INDEX = -2, + NOT_CHANGING_TYPE = -3, + NEW_KEY_INDEX, NEW_VALUE_INDEX, }; @@ -111,7 +116,7 @@ class EditorPropertyArray : public EditorProperty { int page_length = 20; int page_index = 0; - int changing_type_index; + int changing_type_index = EditorPropertyArrayObject::NOT_CHANGING_TYPE; Button *edit = nullptr; PanelContainer *container = nullptr; VBoxContainer *property_vbox = nullptr; @@ -206,7 +211,7 @@ class EditorPropertyDictionary : public EditorProperty { Ref<EditorPropertyDictionaryObject> object; int page_length = 20; int page_index = 0; - int changing_type_index; + int changing_type_index = EditorPropertyDictionaryObject::NOT_CHANGING_TYPE; Button *edit = nullptr; PanelContainer *container = nullptr; VBoxContainer *property_vbox = nullptr; diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index dd698d74b6..742d29fef0 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -138,7 +138,6 @@ void EditorResourcePreview::_preview_ready(const String &p_path, int p_hash, con } Item item; - item.order = order++; item.preview = p_texture; item.small_preview = p_small_texture; item.last_hash = p_hash; @@ -412,7 +411,6 @@ void EditorResourcePreview::queue_edited_resource_preview(const Ref<Resource> &p String path_id = "ID:" + itos(p_res->get_instance_id()); if (cache.has(path_id) && cache[path_id].last_hash == p_res->hash_edited_version_for_preview()) { - cache[path_id].order = order++; p_receiver->call(p_receiver_func, path_id, cache[path_id].preview, cache[path_id].small_preview, p_userdata); return; } @@ -439,7 +437,6 @@ void EditorResourcePreview::queue_resource_preview(const String &p_path, Object MutexLock lock(preview_mutex); if (cache.has(p_path)) { - cache[p_path].order = order++; p_receiver->call(p_receiver_func, p_path, cache[p_path].preview, cache[p_path].small_preview, p_userdata); return; } @@ -533,7 +530,6 @@ void EditorResourcePreview::stop() { EditorResourcePreview::EditorResourcePreview() { singleton = this; - order = 0; } EditorResourcePreview::~EditorResourcePreview() { diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h index 6b67acceaa..2870f9a201 100644 --- a/editor/editor_resource_preview.h +++ b/editor/editor_resource_preview.h @@ -99,13 +99,10 @@ class EditorResourcePreview : public Node { Ref<Texture2D> preview; Ref<Texture2D> small_preview; Dictionary preview_metadata; - int order = 0; uint32_t last_hash = 0; uint64_t modified_time = 0; }; - int order; - HashMap<String, Item> cache; void _preview_ready(const String &p_path, int p_hash, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud, const Dictionary &p_metadata); diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp index 2622645d7c..4862b3436e 100644 --- a/editor/gui/editor_scene_tabs.cpp +++ b/editor/gui/editor_scene_tabs.cpp @@ -405,7 +405,7 @@ EditorSceneTabs::EditorSceneTabs() { scene_tabs->connect(SceneStringName(mouse_exited), callable_mp(this, &EditorSceneTabs::_scene_tab_exit)); scene_tabs->connect(SceneStringName(gui_input), callable_mp(this, &EditorSceneTabs::_scene_tab_input)); scene_tabs->connect("active_tab_rearranged", callable_mp(this, &EditorSceneTabs::_reposition_active_tab)); - scene_tabs->connect(SceneStringName(resized), callable_mp(this, &EditorSceneTabs::_scene_tabs_resized)); + scene_tabs->connect(SceneStringName(resized), callable_mp(this, &EditorSceneTabs::_scene_tabs_resized), CONNECT_DEFERRED); scene_tabs_context_menu = memnew(PopupMenu); tabbar_container->add_child(scene_tabs_context_menu); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 202817f6ee..e4eaab7325 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -922,7 +922,7 @@ void vertex() { VERTEX = VERTEX; POSITION = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * vec4(VERTEX.xyz, 1.0); - POSITION.z = mix(POSITION.z, 0.0, 0.999); + POSITION.z = mix(POSITION.z, POSITION.w, 0.999); POINT_SIZE = point_size; } @@ -1201,7 +1201,7 @@ void vertex() { } VERTEX = VERTEX; POSITION = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * vec4(VERTEX.xyz, 1.0); - POSITION.z = mix(POSITION.z, 0, 0.998); + POSITION.z = mix(POSITION.z, POSITION.w, 0.998); } void fragment() { ALBEDO = COLOR.rgb; diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index cf1ad36adc..6a6e2b83ab 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -743,20 +743,24 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { localization_editor->connect("localization_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); tab_container->add_child(localization_editor); + TabContainer *globals_container = memnew(TabContainer); + globals_container->set_name(TTR("Globals")); + tab_container->add_child(globals_container); + autoload_settings = memnew(EditorAutoloadSettings); autoload_settings->set_name(TTR("Autoload")); autoload_settings->connect("autoload_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); - tab_container->add_child(autoload_settings); + globals_container->add_child(autoload_settings); shaders_global_shader_uniforms_editor = memnew(ShaderGlobalsEditor); shaders_global_shader_uniforms_editor->set_name(TTR("Shader Globals")); shaders_global_shader_uniforms_editor->connect("globals_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); - tab_container->add_child(shaders_global_shader_uniforms_editor); + globals_container->add_child(shaders_global_shader_uniforms_editor); group_settings = memnew(GroupSettingsEditor); - group_settings->set_name(TTR("Global Groups")); + group_settings->set_name(TTR("Groups")); group_settings->connect("group_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); - tab_container->add_child(group_settings); + globals_container->add_child(group_settings); plugin_settings = memnew(EditorPluginSettings); plugin_settings->set_name(TTR("Plugins")); diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp index d123d8ef59..a5157bd394 100644 --- a/editor/property_selector.cpp +++ b/editor/property_selector.cpp @@ -225,11 +225,24 @@ void PropertySelector::_update_search() { } else { Ref<Script> script_ref = Object::cast_to<Script>(ObjectDB::get_instance(script)); if (script_ref.is_valid()) { - methods.push_back(MethodInfo("*Script Methods")); if (script_ref->is_built_in()) { script_ref->reload(true); } - script_ref->get_script_method_list(&methods); + + List<MethodInfo> script_methods; + script_ref->get_script_method_list(&script_methods); + + methods.push_back(MethodInfo("*Script Methods")); // TODO: Split by inheritance. + + for (const MethodInfo &mi : script_methods) { + if (mi.name.begins_with("@")) { + // GH-92782. GDScript inline setters/getters are historically present in `get_method_list()` + // and can be called using `Object.call()`. However, these functions are meant to be internal + // and their names are not valid identifiers, so let's hide them from the user. + continue; + } + methods.push_back(mi); + } } StringName base = base_type; diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 2f57dc5610..c71cb9d4ac 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -2992,6 +2992,10 @@ void SceneTreeDock::_replace_node(Node *p_node, Node *p_by_node, bool p_keep_pro to_erase.push_back(oldnode->get_child(i)); } } + + if (oldnode == edited_scene) { + EditorNode::get_singleton()->set_edited_scene_root(newnode, false); + } oldnode->replace_by(newnode, true); // Re-apply size of anchored control. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs index 04b6c2e743..6b5d7fed78 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs @@ -143,7 +143,7 @@ namespace Godot.NativeInterop if (error.Error != godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_OK) { using godot_variant instanceVariant = VariantUtils.CreateFromGodotObjectPtr(instance); - string where = GetCallErrorWhere(method, &instanceVariant, args, argCount); + string where = GetCallErrorWhere(ref error, method, &instanceVariant, args, argCount); string errorText = GetCallErrorMessage(error, where, args); GD.PushError(errorText); } @@ -161,7 +161,7 @@ namespace Godot.NativeInterop } } - private unsafe static string GetCallErrorWhere(godot_string_name method, godot_variant* instance, godot_variant** args, int argCount) + private unsafe static string GetCallErrorWhere(ref godot_variant_call_error error, godot_string_name method, godot_variant* instance, godot_variant** args, int argCount) { string? methodstr = null; string basestr = GetVariantTypeName(instance); @@ -171,6 +171,10 @@ namespace Godot.NativeInterop if (argCount >= 1) { methodstr = VariantUtils.ConvertToString(*args[0]); + if (error.Error == godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT) + { + error.Argument += 1; + } } } diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index dfbc92a919..1779be2cc2 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -617,7 +617,7 @@ Vector3 NavMap::get_closest_point_to_segment(const Vector3 &p_from, const Vector const Face3 f(p.points[0].pos, p.points[point_id - 1].pos, p.points[point_id].pos); Vector3 inters; if (f.intersects_segment(p_from, p_to, &inters)) { - const real_t d = closest_point_d = p_from.distance_to(inters); + const real_t d = p_from.distance_to(inters); if (use_collision == false) { closest_point = inters; use_collision = true; diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp index b8a2f58935..d9a66aa827 100644 --- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp +++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp @@ -293,7 +293,12 @@ void OpenXRHandTrackingExtension::on_process() { } godot_tracker->set_hand_tracking_source(source); - godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity); + if (location.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) { + godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity); + } else { + godot_tracker->set_has_tracking_data(false); + godot_tracker->invalidate_pose("default"); + } } } } else { diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h index 01da0c7935..087fd1bace 100644 --- a/platform/android/api/jni_singleton.h +++ b/platform/android/api/jni_singleton.h @@ -219,19 +219,12 @@ public: } void add_signal(const StringName &p_name, const Vector<Variant::Type> &p_args) { - if (p_args.size() == 0) { - ADD_SIGNAL(MethodInfo(p_name)); - } else if (p_args.size() == 1) { - ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"))); - } else if (p_args.size() == 2) { - ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"))); - } else if (p_args.size() == 3) { - ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"))); - } else if (p_args.size() == 4) { - ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4"))); - } else if (p_args.size() == 5) { - ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4"), PropertyInfo(p_args[4], "arg5"))); + MethodInfo mi; + mi.name = p_name; + for (int i = 0; i < p_args.size(); i++) { + mi.arguments.push_back(PropertyInfo(p_args[i], "arg" + itos(i + 1))); } + ADD_SIGNAL(mi); } #endif diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index 12d3a6fd2f..aff83ddeee 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -1018,8 +1018,7 @@ void DisplayServerWayland::cursor_set_custom_image(const Ref<Resource> &p_cursor wayland_thread.cursor_shape_clear_custom_image(p_shape); } - Rect2 atlas_rect; - Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect); + Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot); ERR_FAIL_COND(image.is_null()); CustomCursor &cursor = custom_cursors[p_shape]; diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 34b6d0219a..4ca1b11094 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -3108,8 +3108,7 @@ void DisplayServerX11::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu cursors_cache.erase(p_shape); } - Rect2 atlas_rect; - Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect); + Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot); ERR_FAIL_COND(image.is_null()); Vector2i texture_size = image->get_size(); @@ -3127,13 +3126,8 @@ void DisplayServerX11::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu cursor_image->pixels = (XcursorPixel *)memalloc(size); for (XcursorPixel index = 0; index < image_size; index++) { - int row_index = floor(index / texture_size.width) + atlas_rect.position.y; - int column_index = (index % int(texture_size.width)) + atlas_rect.position.x; - - if (atlas_rect.has_area()) { - column_index = MIN(column_index, atlas_rect.size.width - 1); - row_index = MIN(row_index, atlas_rect.size.height - 1); - } + int row_index = floor(index / texture_size.width); + int column_index = index % int(texture_size.width); *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32(); } diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 50313cfe67..1032766480 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -2845,8 +2845,7 @@ void DisplayServerMacOS::cursor_set_custom_image(const Ref<Resource> &p_cursor, cursors_cache.erase(p_shape); } - Rect2 atlas_rect; - Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect); + Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot); ERR_FAIL_COND(image.is_null()); Vector2i texture_size = image->get_size(); @@ -2868,13 +2867,8 @@ void DisplayServerMacOS::cursor_set_custom_image(const Ref<Resource> &p_cursor, int len = int(texture_size.width * texture_size.height); for (int i = 0; i < len; i++) { - int row_index = floor(i / texture_size.width) + atlas_rect.position.y; - int column_index = (i % int(texture_size.width)) + atlas_rect.position.x; - - if (atlas_rect.has_area()) { - column_index = MIN(column_index, atlas_rect.size.width - 1); - row_index = MIN(row_index, atlas_rect.size.height - 1); - } + int row_index = floor(i / texture_size.width); + int column_index = i % int(texture_size.width); uint32_t color = image->get_pixel(column_index, row_index).to_argb32(); @@ -3172,42 +3166,13 @@ void DisplayServerMacOS::set_icon(const Ref<Image> &p_icon) { DisplayServer::IndicatorID DisplayServerMacOS::create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback) { NSImage *nsimg = nullptr; - if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0) { + if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) { Ref<Image> img = p_icon->get_image(); img = img->duplicate(); - img->convert(Image::FORMAT_RGBA8); - - NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:nullptr - pixelsWide:img->get_width() - pixelsHigh:img->get_height() - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:img->get_width() * 4 - bitsPerPixel:32]; - if (imgrep) { - uint8_t *pixels = [imgrep bitmapData]; - - int len = img->get_width() * img->get_height(); - const uint8_t *r = img->get_data().ptr(); - - /* Premultiply the alpha channel */ - for (int i = 0; i < len; i++) { - uint8_t alpha = r[i * 4 + 3]; - pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255); - pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255); - pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255); - pixels[i * 4 + 3] = alpha; - } - - nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())]; - if (nsimg) { - [nsimg addRepresentation:imgrep]; - } + if (img->is_compressed()) { + img->decompress(); } + nsimg = _convert_to_nsimg(img); } IndicatorData idat; @@ -3235,42 +3200,13 @@ void DisplayServerMacOS::status_indicator_set_icon(IndicatorID p_id, const Ref<T ERR_FAIL_COND(!indicators.has(p_id)); NSImage *nsimg = nullptr; - if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0) { + if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) { Ref<Image> img = p_icon->get_image(); img = img->duplicate(); - img->convert(Image::FORMAT_RGBA8); - - NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:nullptr - pixelsWide:img->get_width() - pixelsHigh:img->get_height() - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:img->get_width() * 4 - bitsPerPixel:32]; - if (imgrep) { - uint8_t *pixels = [imgrep bitmapData]; - - int len = img->get_width() * img->get_height(); - const uint8_t *r = img->get_data().ptr(); - - /* Premultiply the alpha channel */ - for (int i = 0; i < len; i++) { - uint8_t alpha = r[i * 4 + 3]; - pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255); - pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255); - pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255); - pixels[i * 4 + 3] = alpha; - } - - nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())]; - if (nsimg) { - [nsimg addRepresentation:imgrep]; - } + if (img->is_compressed()) { + img->decompress(); } + nsimg = _convert_to_nsimg(img); } NSStatusItem *item = indicators[p_id].item; diff --git a/platform/macos/godot_menu_delegate.mm b/platform/macos/godot_menu_delegate.mm index 5c1e849715..3f7dfac3de 100644 --- a/platform/macos/godot_menu_delegate.mm +++ b/platform/macos/godot_menu_delegate.mm @@ -40,13 +40,20 @@ - (void)doNothing:(id)sender { } -- (void)menuNeedsUpdate:(NSMenu *)menu { +- (void)menuWillOpen:(NSMenu *)menu { if (NativeMenu::get_singleton()) { NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton(); nmenu->_menu_open(menu); } } +- (void)menuNeedsUpdate:(NSMenu *)menu { + if (NativeMenu::get_singleton()) { + NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton(); + nmenu->_menu_need_update(menu); + } +} + - (void)menuDidClose:(NSMenu *)menu { if (NativeMenu::get_singleton()) { NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton(); diff --git a/platform/macos/native_menu_macos.h b/platform/macos/native_menu_macos.h index b5dbb8b9b0..42cf6740d9 100644 --- a/platform/macos/native_menu_macos.h +++ b/platform/macos/native_menu_macos.h @@ -73,8 +73,11 @@ class NativeMenuMacOS : public NativeMenu { public: void _register_system_menus(NSMenu *p_main_menu, NSMenu *p_application_menu, NSMenu *p_window_menu, NSMenu *p_help_menu, NSMenu *p_dock_menu); NSMenu *_get_dock_menu(); + + void _menu_need_update(NSMenu *p_menu); void _menu_open(NSMenu *p_menu); void _menu_close(NSMenu *p_menu); + void _menu_close_cb(const RID &p_rid); virtual bool has_feature(Feature p_feature) const override; @@ -98,6 +101,8 @@ public: virtual void set_minimum_width(const RID &p_rid, float p_width) override; virtual float get_minimum_width(const RID &p_rid) const override; + virtual bool is_opened(const RID &p_rid) const override; + virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1) override; virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; diff --git a/platform/macos/native_menu_macos.mm b/platform/macos/native_menu_macos.mm index 1cf13a2d69..1ae1137ca0 100644 --- a/platform/macos/native_menu_macos.mm +++ b/platform/macos/native_menu_macos.mm @@ -91,11 +91,22 @@ void NativeMenuMacOS::_menu_open(NSMenu *p_menu) { if (menu_lookup.has(p_menu)) { MenuData *md = menus.get_or_null(menu_lookup[p_menu]); if (md) { + // Note: Set "is_open" flag, but do not call callback, menu items can't be modified during this call and "_menu_need_update" will be called right before it. md->is_open = true; + } + } +} + +void NativeMenuMacOS::_menu_need_update(NSMenu *p_menu) { + if (menu_lookup.has(p_menu)) { + MenuData *md = menus.get_or_null(menu_lookup[p_menu]); + if (md) { + // Note: "is_open" flag is set by "_menu_open", this method is always called before menu is shown, but might be called for the other reasons as well. if (md->open_cb.is_valid()) { Variant ret; Callable::CallError ce; + // Callback is called directly, since it's expected to modify menu items before it's shown. md->open_cb.callp(nullptr, 0, ret, ce); if (ce.error != Callable::CallError::CALL_OK) { ERR_PRINT(vformat("Failed to execute menu open callback: %s.", Variant::get_callable_error_text(md->open_cb, nullptr, 0, ce))); @@ -110,15 +121,22 @@ void NativeMenuMacOS::_menu_close(NSMenu *p_menu) { MenuData *md = menus.get_or_null(menu_lookup[p_menu]); if (md) { md->is_open = false; - if (md->close_cb.is_valid()) { - Variant ret; - Callable::CallError ce; - md->close_cb.callp(nullptr, 0, ret, ce); - if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT(vformat("Failed to execute menu close callback: %s.", Variant::get_callable_error_text(md->close_cb, nullptr, 0, ce))); - } - } + // Callback called deferred, since it should not modify menu items during "_menu_close" call. + callable_mp(this, &NativeMenuMacOS::_menu_close_cb).call_deferred(menu_lookup[p_menu]); + } + } +} + +void NativeMenuMacOS::_menu_close_cb(const RID &p_rid) { + MenuData *md = menus.get_or_null(p_rid); + if (md->close_cb.is_valid()) { + Variant ret; + Callable::CallError ce; + + md->close_cb.callp(nullptr, 0, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute menu close callback: %s.", Variant::get_callable_error_text(md->close_cb, nullptr, 0, ce))); } } } @@ -328,6 +346,13 @@ float NativeMenuMacOS::get_minimum_width(const RID &p_rid) const { return md->menu.minimumWidth * DisplayServer::get_singleton()->screen_get_max_scale(); } +bool NativeMenuMacOS::is_opened(const RID &p_rid) const { + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, false); + + return md->is_open; +} + int NativeMenuMacOS::add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag, int p_index) { MenuData *md = menus.get_or_null(p_rid); MenuData *md_sub = menus.get_or_null(p_submenu_rid); @@ -436,7 +461,7 @@ int NativeMenuMacOS::add_icon_item(const RID &p_rid, const Ref<Texture2D> &p_ico obj->max_states = 0; obj->state = 0; DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (ds && p_icon.is_valid()) { + if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) { obj->img = p_icon->get_image(); obj->img = obj->img->duplicate(); if (obj->img->is_compressed()) { @@ -467,7 +492,7 @@ int NativeMenuMacOS::add_icon_check_item(const RID &p_rid, const Ref<Texture2D> obj->max_states = 0; obj->state = 0; DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (ds && p_icon.is_valid()) { + if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) { obj->img = p_icon->get_image(); obj->img = obj->img->duplicate(); if (obj->img->is_compressed()) { @@ -518,7 +543,7 @@ int NativeMenuMacOS::add_icon_radio_check_item(const RID &p_rid, const Ref<Textu obj->max_states = 0; obj->state = 0; DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (ds && p_icon.is_valid()) { + if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) { obj->img = p_icon->get_image(); obj->img = obj->img->duplicate(); if (obj->img->is_compressed()) { @@ -1212,7 +1237,7 @@ void NativeMenuMacOS::set_item_icon(const RID &p_rid, int p_idx, const Ref<Textu GodotMenuItem *obj = [menu_item representedObject]; ERR_FAIL_NULL(obj); DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (ds && p_icon.is_valid()) { + if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) { obj->img = p_icon->get_image(); obj->img = obj->img->duplicate(); if (obj->img->is_compressed()) { diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index fab92b1894..40de4e523b 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -517,19 +517,10 @@ DisplayServer::CursorShape DisplayServerWeb::cursor_get_shape() const { void DisplayServerWeb::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { ERR_FAIL_INDEX(p_shape, CURSOR_MAX); if (p_cursor.is_valid()) { - Rect2 atlas_rect; - Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect); + Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot); ERR_FAIL_COND(image.is_null()); Vector2i texture_size = image->get_size(); - if (atlas_rect.has_area()) { - image->crop_from_point( - atlas_rect.position.x, - atlas_rect.position.y, - texture_size.width, - texture_size.height); - } - if (image->get_format() != Image::FORMAT_RGBA8) { image->convert(Image::FORMAT_RGBA8); } diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 4a482f560c..73987c44db 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -2408,8 +2408,7 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor cursors_cache.erase(p_shape); } - Rect2 atlas_rect; - Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect); + Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot); ERR_FAIL_COND(image.is_null()); Vector2i texture_size = image->get_size(); @@ -2437,13 +2436,9 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor bool fully_transparent = true; for (UINT index = 0; index < image_size; index++) { - int row_index = floor(index / texture_size.width) + atlas_rect.position.y; - int column_index = (index % int(texture_size.width)) + atlas_rect.position.x; + int row_index = floor(index / texture_size.width); + int column_index = index % int(texture_size.width); - if (atlas_rect.has_area()) { - column_index = MIN(column_index, atlas_rect.size.width - 1); - row_index = MIN(row_index, atlas_rect.size.height - 1); - } const Color &c = image->get_pixel(column_index, row_index); fully_transparent = fully_transparent && (c.a == 0.f); @@ -3171,9 +3166,12 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) { DisplayServer::IndicatorID DisplayServerWindows::create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback) { HICON hicon = nullptr; - if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0) { + if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) { Ref<Image> img = p_icon->get_image(); img = img->duplicate(); + if (img->is_compressed()) { + img->decompress(); + } img->convert(Image::FORMAT_RGBA8); int w = img->get_width(); @@ -3241,9 +3239,12 @@ void DisplayServerWindows::status_indicator_set_icon(IndicatorID p_id, const Ref ERR_FAIL_COND(!indicators.has(p_id)); HICON hicon = nullptr; - if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0) { + if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) { Ref<Image> img = p_icon->get_image(); img = img->duplicate(); + if (img->is_compressed()) { + img->decompress(); + } img->convert(Image::FORMAT_RGBA8); int w = img->get_width(); diff --git a/platform/windows/native_menu_windows.cpp b/platform/windows/native_menu_windows.cpp index 40a08f87df..d9dc28e9d9 100644 --- a/platform/windows/native_menu_windows.cpp +++ b/platform/windows/native_menu_windows.cpp @@ -35,6 +35,8 @@ #include "scene/resources/image_texture.h" HBITMAP NativeMenuWindows::_make_bitmap(const Ref<Image> &p_img) const { + p_img->convert(Image::FORMAT_RGBA8); + Vector2i texture_size = p_img->get_size(); UINT image_size = texture_size.width * texture_size.height; @@ -231,6 +233,11 @@ float NativeMenuWindows::get_minimum_width(const RID &p_rid) const { return 0.f; } +bool NativeMenuWindows::is_opened(const RID &p_rid) const { + // Not supported. + return false; +} + int NativeMenuWindows::add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag, int p_index) { MenuData *md = menus.get_or_null(p_rid); MenuData *md_sub = menus.get_or_null(p_submenu_rid); @@ -349,12 +356,14 @@ int NativeMenuWindows::add_icon_item(const RID &p_rid, const Ref<Texture2D> &p_i item_data->checkable_type = CHECKABLE_TYPE_NONE; item_data->max_states = 0; item_data->state = 0; - item_data->img = p_icon->get_image(); - item_data->img = item_data->img->duplicate(); - if (item_data->img->is_compressed()) { - item_data->img->decompress(); + if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) { + item_data->img = p_icon->get_image(); + item_data->img = item_data->img->duplicate(); + if (item_data->img->is_compressed()) { + item_data->img->decompress(); + } + item_data->bmp = _make_bitmap(item_data->img); } - item_data->bmp = _make_bitmap(item_data->img); Char16String label = p_label.utf16(); MENUITEMINFOW item; @@ -389,12 +398,14 @@ int NativeMenuWindows::add_icon_check_item(const RID &p_rid, const Ref<Texture2D item_data->checkable_type = CHECKABLE_TYPE_CHECK_BOX; item_data->max_states = 0; item_data->state = 0; - item_data->img = p_icon->get_image(); - item_data->img = item_data->img->duplicate(); - if (item_data->img->is_compressed()) { - item_data->img->decompress(); + if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) { + item_data->img = p_icon->get_image(); + item_data->img = item_data->img->duplicate(); + if (item_data->img->is_compressed()) { + item_data->img->decompress(); + } + item_data->bmp = _make_bitmap(item_data->img); } - item_data->bmp = _make_bitmap(item_data->img); Char16String label = p_label.utf16(); MENUITEMINFOW item; @@ -462,12 +473,14 @@ int NativeMenuWindows::add_icon_radio_check_item(const RID &p_rid, const Ref<Tex item_data->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; item_data->max_states = 0; item_data->state = 0; - item_data->img = p_icon->get_image(); - item_data->img = item_data->img->duplicate(); - if (item_data->img->is_compressed()) { - item_data->img->decompress(); + if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) { + item_data->img = p_icon->get_image(); + item_data->img = item_data->img->duplicate(); + if (item_data->img->is_compressed()) { + item_data->img->decompress(); + } + item_data->bmp = _make_bitmap(item_data->img); } - item_data->bmp = _make_bitmap(item_data->img); Char16String label = p_label.utf16(); MENUITEMINFOW item; @@ -1082,7 +1095,7 @@ void NativeMenuWindows::set_item_icon(const RID &p_rid, int p_idx, const Ref<Tex if (item_data->bmp) { DeleteObject(item_data->bmp); } - if (p_icon.is_valid()) { + if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) { item_data->img = p_icon->get_image(); item_data->img = item_data->img->duplicate(); if (item_data->img->is_compressed()) { diff --git a/platform/windows/native_menu_windows.h b/platform/windows/native_menu_windows.h index 74fd231903..5c4aaa52c8 100644 --- a/platform/windows/native_menu_windows.h +++ b/platform/windows/native_menu_windows.h @@ -90,6 +90,8 @@ public: virtual void set_minimum_width(const RID &p_rid, float p_width) override; virtual float get_minimum_width(const RID &p_rid) const override; + virtual bool is_opened(const RID &p_rid) const override; + virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1) override; virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index d8fcbbb883..b7db730b49 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -243,8 +243,8 @@ Size2 BoxContainer::get_minimum_size() const { bool first = true; for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible() || c->is_set_as_top_level()) { + Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); + if (!c) { continue; } diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index f1faf3e899..c328022d4f 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -139,9 +139,15 @@ void Container::queue_sort() { pending_sort = true; } -Control *Container::as_sortable_control(Node *p_node) const { +Control *Container::as_sortable_control(Node *p_node, SortableVisbilityMode p_visibility_mode) const { Control *c = Object::cast_to<Control>(p_node); - if (!c || !c->is_visible_in_tree() || c->is_set_as_top_level()) { + if (!c || c->is_set_as_top_level()) { + return nullptr; + } + if (p_visibility_mode == SortableVisbilityMode::VISIBLE && !c->is_visible()) { + return nullptr; + } + if (p_visibility_mode == SortableVisbilityMode::VISIBLE_IN_TREE && !c->is_visible_in_tree()) { return nullptr; } return c; @@ -177,13 +183,9 @@ Vector<int> Container::get_allowed_size_flags_vertical() const { void Container::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - pending_sort = false; - queue_sort(); - } break; - case NOTIFICATION_RESIZED: - case NOTIFICATION_THEME_CHANGED: { + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_ENTER_TREE: { queue_sort(); } break; diff --git a/scene/gui/container.h b/scene/gui/container.h index 405220cee6..0561d0d219 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -41,8 +41,14 @@ class Container : public Control { void _child_minsize_changed(); protected: + enum class SortableVisbilityMode { + VISIBLE, + VISIBLE_IN_TREE, + IGNORE, + }; + void queue_sort(); - Control *as_sortable_control(Node *p_node) const; + Control *as_sortable_control(Node *p_node, SortableVisbilityMode p_visibility_mode = SortableVisbilityMode::VISIBLE_IN_TREE) const; virtual void add_child_notify(Node *p_child) override; virtual void move_child_notify(Node *p_child) override; diff --git a/scene/gui/graph_element.cpp b/scene/gui/graph_element.cpp index e231b05d7f..b63ed8d1ad 100644 --- a/scene/gui/graph_element.cpp +++ b/scene/gui/graph_element.cpp @@ -60,8 +60,8 @@ void GraphElement::_resort() { Size2 GraphElement::get_minimum_size() const { Size2 minsize; for (int i = 0; i < get_child_count(); i++) { - Control *child = Object::cast_to<Control>(get_child(i)); - if (!child || child->is_set_as_top_level()) { + Control *child = as_sortable_control(get_child(i), SortableVisbilityMode::IGNORE); + if (!child) { continue; } diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index d804f83e1c..72e59bfc8a 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -130,8 +130,8 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const { void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const { int idx = 0; for (int i = 0; i < get_child_count(false); i++) { - Control *child = Object::cast_to<Control>(get_child(i, false)); - if (!child || child->is_set_as_top_level()) { + Control *child = as_sortable_control(get_child(i, false), SortableVisbilityMode::IGNORE); + if (!child) { continue; } @@ -658,8 +658,8 @@ void GraphNode::_port_pos_update() { int slot_index = 0; for (int i = 0; i < get_child_count(false); i++) { - Control *child = Object::cast_to<Control>(get_child(i, false)); - if (!child || child->is_set_as_top_level()) { + Control *child = as_sortable_control(get_child(i, false), SortableVisbilityMode::IGNORE); + if (!child) { continue; } diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp index 06e4a7cc13..a47b131708 100644 --- a/scene/gui/margin_container.cpp +++ b/scene/gui/margin_container.cpp @@ -36,8 +36,8 @@ Size2 MarginContainer::get_minimum_size() const { Size2 max; for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible() || c->is_set_as_top_level()) { + Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); + if (!c) { continue; } diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp index 76fde26b26..2c39e148a0 100644 --- a/scene/gui/panel_container.cpp +++ b/scene/gui/panel_container.cpp @@ -35,7 +35,7 @@ Size2 PanelContainer::get_minimum_size() const { Size2 ms; for (int i = 0; i < get_child_count(); i++) { - Control *c = as_sortable_control(get_child(i)); + Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); if (!c) { continue; } diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 1f4d1dbf52..824bb77694 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -42,7 +42,7 @@ Size2 ScrollContainer::get_minimum_size() const { largest_child_min_size = Size2(); for (int i = 0; i < get_child_count(); i++) { - Control *c = as_sortable_control(get_child(i)); + Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); if (!c) { continue; } diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index d0c3f3d65e..58724cf4e9 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -362,8 +362,8 @@ void TabContainer::_on_mouse_exited() { Vector<Control *> TabContainer::_get_tab_controls() const { Vector<Control *> controls; for (int i = 0; i < get_child_count(); i++) { - Control *control = Object::cast_to<Control>(get_child(i)); - if (!control || control->is_set_as_top_level() || control == tab_bar || children_removing.has(control)) { + Control *control = as_sortable_control(get_child(i), SortableVisbilityMode::IGNORE); + if (!control || control == tab_bar || children_removing.has(control)) { continue; } @@ -539,8 +539,8 @@ void TabContainer::add_child_notify(Node *p_child) { return; } - Control *c = Object::cast_to<Control>(p_child); - if (!c || c->is_set_as_top_level()) { + Control *c = as_sortable_control(p_child, SortableVisbilityMode::IGNORE); + if (!c) { return; } c->hide(); @@ -569,8 +569,8 @@ void TabContainer::move_child_notify(Node *p_child) { return; } - Control *c = Object::cast_to<Control>(p_child); - if (c && !c->is_set_as_top_level()) { + Control *c = as_sortable_control(p_child, SortableVisbilityMode::IGNORE); + if (c) { tab_bar->move_tab(c->get_meta("_tab_index"), get_tab_idx_from_control(c)); } @@ -584,8 +584,8 @@ void TabContainer::remove_child_notify(Node *p_child) { return; } - Control *c = Object::cast_to<Control>(p_child); - if (!c || c->is_set_as_top_level()) { + Control *c = as_sortable_control(p_child, SortableVisbilityMode::IGNORE); + if (!c) { return; } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index a46d77c61e..2b2ea54dde 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -286,6 +286,7 @@ void TextEdit::Text::invalidate_all_lines() { } tab_size_dirty = false; + max_width = -1; _calculate_max_line_width(); } @@ -404,10 +405,12 @@ void TextEdit::Text::remove_range(int p_from_line, int p_to_line) { text.resize(text.size() - diff); if (dirty_height) { + line_height = -1; _calculate_line_height(); } if (dirty_width) { + max_width = -1; _calculate_max_line_width(); } } @@ -5174,7 +5177,7 @@ void TextEdit::add_selection_for_next_occurrence() { const String &highlighted_text = get_selected_text(caret); int column = get_selection_from_column(caret) + 1; - int line = get_caret_line(caret); + int line = get_selection_from_line(caret); const Point2i next_occurrence = search(highlighted_text, SEARCH_MATCH_CASE, line, column); @@ -5188,6 +5191,7 @@ void TextEdit::add_selection_for_next_occurrence() { if (new_caret != -1) { select(next_occurrence.y, next_occurrence.x, next_occurrence.y, end, new_caret); + _unhide_carets(); adjust_viewport_to_caret(new_caret); merge_overlapping_carets(); } @@ -5211,8 +5215,8 @@ void TextEdit::skip_selection_for_next_occurrence() { // Due to const and &(reference) presence, ternary operator is a way to avoid errors and warnings. const String &searched_text = has_selection(caret) ? get_selected_text(caret) : get_word_under_caret(caret); - int column = (has_selection(caret) ? get_selection_from_column(caret) : get_caret_column(caret)) + 1; - int line = get_caret_line(caret); + int column = get_selection_from_column(caret) + 1; + int line = get_selection_from_line(caret); const Point2i next_occurrence = search(searched_text, SEARCH_MATCH_CASE, line, column); @@ -5220,12 +5224,13 @@ void TextEdit::skip_selection_for_next_occurrence() { return; } - int to_column = (has_selection(caret) ? get_selection_to_column(caret) : get_caret_column(caret)) + 1; + int to_column = get_selection_to_column(caret) + 1; int end = next_occurrence.x + (to_column - column); int new_caret = add_caret(next_occurrence.y, end); if (new_caret != -1) { select(next_occurrence.y, next_occurrence.x, next_occurrence.y, end, new_caret); + _unhide_carets(); adjust_viewport_to_caret(new_caret); merge_overlapping_carets(); } diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 601e8c52a4..277d568aaf 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -2204,6 +2204,10 @@ Error VisualShader::_write_node(Type type, StringBuilder *p_global_code, StringB Vector3 val = defval; inputs[i] = "n_in" + itos(p_node) + "p" + itos(i); node_code += " vec3 " + inputs[i] + " = " + vformat("vec3(%.5f, %.5f, %.5f);\n", val.x, val.y, val.z); + } else if (defval.get_type() == Variant::VECTOR4) { + Vector4 val = defval; + inputs[i] = "n_in" + itos(p_node) + "p" + itos(i); + node_code += " vec4 " + inputs[i] + " = " + vformat("vec4(%.5f, %.5f, %.5f, %.5f);\n", val.x, val.y, val.z, val.w); } else if (defval.get_type() == Variant::QUATERNION) { Quaternion val = defval; inputs[i] = "n_in" + itos(p_node) + "p" + itos(i); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index d5394c8af5..cb8719fbef 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -7825,7 +7825,7 @@ bool VisualShaderNodeDistanceFade::has_output_port_preview(int p_port) const { String VisualShaderNodeDistanceFade::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; - code += vformat(" %s = clamp(smoothstep(%s, %s,-VERTEX.z),0.0,1.0);\n", p_output_vars[0], p_input_vars[0], p_input_vars[1]); + code += vformat(" %s = clamp(smoothstep(%s, %s, length(VERTEX)), 0.0, 1.0);\n", p_output_vars[0], p_input_vars[0], p_input_vars[1]); return code; } diff --git a/servers/display/native_menu.cpp b/servers/display/native_menu.cpp index ca46560c7c..c7346637d8 100644 --- a/servers/display/native_menu.cpp +++ b/servers/display/native_menu.cpp @@ -56,6 +56,8 @@ void NativeMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_minimum_width", "rid", "width"), &NativeMenu::set_minimum_width); ClassDB::bind_method(D_METHOD("get_minimum_width", "rid"), &NativeMenu::get_minimum_width); + ClassDB::bind_method(D_METHOD("is_opened", "rid"), &NativeMenu::is_opened); + ClassDB::bind_method(D_METHOD("add_submenu_item", "rid", "label", "submenu_rid", "tag", "index"), &NativeMenu::add_submenu_item, DEFVAL(Variant()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("add_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("add_check_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); @@ -200,6 +202,11 @@ Callable NativeMenu::get_popup_close_callback(const RID &p_rid) const { return Callable(); } +bool NativeMenu::is_opened(const RID &p_rid) const { + WARN_PRINT("Global menus are not supported on this platform."); + return false; +} + void NativeMenu::set_minimum_width(const RID &p_rid, float p_width) { WARN_PRINT("Global menus are not supported on this platform."); } diff --git a/servers/display/native_menu.h b/servers/display/native_menu.h index 2bc061a216..29d22e03aa 100644 --- a/servers/display/native_menu.h +++ b/servers/display/native_menu.h @@ -90,6 +90,8 @@ public: virtual void set_minimum_width(const RID &p_rid, float p_width); virtual float get_minimum_width(const RID &p_rid) const; + virtual bool is_opened(const RID &p_rid) const; + virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1); virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 2150a3038e..b6e0d58af7 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -31,7 +31,6 @@ #include "display_server.h" #include "core/input/input.h" -#include "scene/resources/atlas_texture.h" #include "scene/resources/texture.h" #include "servers/display_server_headless.h" @@ -1124,35 +1123,22 @@ void DisplayServer::_bind_methods() { BIND_ENUM_CONSTANT(TTS_UTTERANCE_BOUNDARY); } -Ref<Image> DisplayServer::_get_cursor_image_from_resource(const Ref<Resource> &p_cursor, const Vector2 &p_hotspot, Rect2 &r_atlas_rect) { +Ref<Image> DisplayServer::_get_cursor_image_from_resource(const Ref<Resource> &p_cursor, const Vector2 &p_hotspot) { Ref<Image> image; ERR_FAIL_COND_V_MSG(p_hotspot.x < 0 || p_hotspot.y < 0, image, "Hotspot outside cursor image."); - Size2 texture_size; - Ref<Texture2D> texture = p_cursor; if (texture.is_valid()) { - Ref<AtlasTexture> atlas_texture = p_cursor; - - if (atlas_texture.is_valid()) { - texture = atlas_texture->get_atlas(); - r_atlas_rect.size = texture->get_size(); - r_atlas_rect.position = atlas_texture->get_region().position; - texture_size = atlas_texture->get_region().size; - } else { - texture_size = texture->get_size(); - } image = texture->get_image(); } else { image = p_cursor; - ERR_FAIL_COND_V(image.is_null(), image); - texture_size = image->get_size(); } + ERR_FAIL_COND_V(image.is_null(), image); - ERR_FAIL_COND_V_MSG(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height, image, "Hotspot outside cursor image."); - ERR_FAIL_COND_V_MSG(texture_size.width > 256 || texture_size.height > 256, image, "Cursor image too big. Max supported size is 256x256."); + Size2 image_size = image->get_size(); + ERR_FAIL_COND_V_MSG(p_hotspot.x > image_size.width || p_hotspot.y > image_size.height, image, "Hotspot outside cursor image."); + ERR_FAIL_COND_V_MSG(image_size.width > 256 || image_size.height > 256, image, "Cursor image too big. Max supported size is 256x256."); - ERR_FAIL_COND_V(image.is_null(), image); if (image->is_compressed()) { image = image->duplicate(true); Error err = image->decompress(); diff --git a/servers/display_server.h b/servers/display_server.h index 5224d59c04..5d82b6c13c 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -101,7 +101,7 @@ private: protected: static void _bind_methods(); - static Ref<Image> _get_cursor_image_from_resource(const Ref<Resource> &p_cursor, const Vector2 &p_hotspot, Rect2 &r_atlas_rect); + static Ref<Image> _get_cursor_image_from_resource(const Ref<Resource> &p_cursor, const Vector2 &p_hotspot); enum { MAX_SERVERS = 64 @@ -248,8 +248,8 @@ public: virtual void tts_set_utterance_callback(TTSUtteranceEvent p_event, const Callable &p_callable); virtual void tts_post_utterance_event(TTSUtteranceEvent p_event, int p_id, int p_pos = 0); - virtual bool is_dark_mode_supported() const { return false; }; - virtual bool is_dark_mode() const { return false; }; + virtual bool is_dark_mode_supported() const { return false; } + virtual bool is_dark_mode() const { return false; } virtual Color get_accent_color() const { return Color(0, 0, 0, 0); } virtual Color get_base_color() const { return Color(0, 0, 0, 0); } virtual void set_system_theme_change_callback(const Callable &p_callable) {} @@ -338,8 +338,8 @@ public: return scale; } virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0; - virtual Color screen_get_pixel(const Point2i &p_position) const { return Color(); }; - virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const { return Ref<Image>(); }; + virtual Color screen_get_pixel(const Point2i &p_position) const { return Color(); } + virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const { return Ref<Image>(); } virtual bool is_touchscreen_available() const; // Keep the ScreenOrientation enum values in sync with the `display/window/handheld/orientation` @@ -398,9 +398,9 @@ public: virtual void show_window(WindowID p_id); virtual void delete_sub_window(WindowID p_id); - virtual WindowID window_get_active_popup() const { return INVALID_WINDOW_ID; }; - virtual void window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect){}; - virtual Rect2i window_get_popup_safe_rect(WindowID p_window) const { return Rect2i(); }; + virtual WindowID window_get_active_popup() const { return INVALID_WINDOW_ID; } + virtual void window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) {} + virtual Rect2i window_get_popup_safe_rect(WindowID p_window) const { return Rect2i(); } virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const; @@ -555,10 +555,10 @@ public: virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const; virtual Key keyboard_get_label_from_physical(Key p_keycode) const; - virtual int tablet_get_driver_count() const { return 1; }; - virtual String tablet_get_driver_name(int p_driver) const { return "default"; }; - virtual String tablet_get_current_driver() const { return "default"; }; - virtual void tablet_set_current_driver(const String &p_driver){}; + virtual int tablet_get_driver_count() const { return 1; } + virtual String tablet_get_driver_name(int p_driver) const { return "default"; } + virtual String tablet_get_current_driver() const { return "default"; } + virtual void tablet_set_current_driver(const String &p_driver) {} virtual void process_events() = 0; diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h index c02830b6df..317dbe9ab9 100644 --- a/tests/scene/test_code_edit.h +++ b/tests/scene/test_code_edit.h @@ -3331,6 +3331,45 @@ TEST_CASE("[SceneTree][CodeEdit] folding") { CHECK_FALSE(code_edit->is_line_folded(1)); } + SUBCASE("[CodeEdit] actions unfold") { + // add_selection_for_next_occurrence unfolds. + code_edit->set_text("test\n\tline1 test\n\t\tline 2\ntest2"); + code_edit->select(0, 0, 0, 4); + code_edit->fold_line(0); + CHECK(code_edit->is_line_folded(0)); + code_edit->add_selection_for_next_occurrence(); + + CHECK(code_edit->get_caret_count() == 2); + CHECK(code_edit->has_selection(0)); + CHECK(code_edit->get_caret_line() == 0); + CHECK(code_edit->get_selection_origin_line() == 0); + CHECK(code_edit->get_caret_column() == 4); + CHECK(code_edit->get_selection_origin_column() == 0); + CHECK(code_edit->has_selection(1)); + CHECK(code_edit->get_caret_line(1) == 1); + CHECK(code_edit->get_selection_origin_line(1) == 1); + CHECK(code_edit->get_caret_column(1) == 11); + CHECK(code_edit->get_selection_origin_column(1) == 7); + CHECK_FALSE(code_edit->is_line_folded(0)); + code_edit->remove_secondary_carets(); + + // skip_selection_for_next_occurrence unfolds. + code_edit->select(0, 0, 0, 4); + code_edit->fold_line(0); + CHECK(code_edit->is_line_folded(0)); + code_edit->skip_selection_for_next_occurrence(); + + CHECK(code_edit->get_caret_count() == 1); + CHECK(code_edit->has_selection(0)); + CHECK(code_edit->get_caret_line() == 1); + CHECK(code_edit->get_selection_origin_line() == 1); + CHECK(code_edit->get_caret_column() == 11); + CHECK(code_edit->get_selection_origin_column() == 7); + CHECK_FALSE(code_edit->is_line_folded(0)); + code_edit->remove_secondary_carets(); + code_edit->deselect(); + } + SUBCASE("[CodeEdit] toggle folding carets") { code_edit->set_text("test\n\tline1\ntest2\n\tline2"); diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h index 8778ea86a6..cf6b89c330 100644 --- a/tests/servers/test_navigation_server_3d.h +++ b/tests/servers/test_navigation_server_3d.h @@ -697,12 +697,16 @@ TEST_SUITE("[Navigation]") { CHECK_NE(navigation_server->map_get_closest_point(map, Vector3(0, 0, 0)), Vector3(0, 0, 0)); CHECK_NE(navigation_server->map_get_closest_point_normal(map, Vector3(0, 0, 0)), Vector3()); CHECK(navigation_server->map_get_closest_point_owner(map, Vector3(0, 0, 0)).is_valid()); - // TODO: Test map_get_closest_point_to_segment() with p_use_collision=true as well. CHECK_NE(navigation_server->map_get_closest_point_to_segment(map, Vector3(0, 0, 0), Vector3(1, 1, 1), false), Vector3()); + CHECK_NE(navigation_server->map_get_closest_point_to_segment(map, Vector3(0, 0, 0), Vector3(1, 1, 1), true), Vector3()); CHECK_NE(navigation_server->map_get_path(map, Vector3(0, 0, 0), Vector3(10, 0, 10), true).size(), 0); CHECK_NE(navigation_server->map_get_path(map, Vector3(0, 0, 0), Vector3(10, 0, 10), false).size(), 0); } + SUBCASE("'map_get_closest_point_to_segment' with 'use_collision' should return default if segment doesn't intersect map") { + CHECK_EQ(navigation_server->map_get_closest_point_to_segment(map, Vector3(1, 2, 1), Vector3(1, 1, 1), true), Vector3()); + } + SUBCASE("Elaborate query with 'CORRIDORFUNNEL' post-processing should yield non-empty result") { Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D); query_parameters->set_map(map); diff --git a/thirdparty/embree/common/sys/sysinfo.cpp b/thirdparty/embree/common/sys/sysinfo.cpp index d01eab3c9d..4ecab05265 100644 --- a/thirdparty/embree/common/sys/sysinfo.cpp +++ b/thirdparty/embree/common/sys/sysinfo.cpp @@ -295,7 +295,7 @@ namespace embree if (nIds >= 1) __cpuid (cpuid_leaf_1,0x00000001); #if _WIN32 #if _MSC_VER && (_MSC_FULL_VER < 160040219) -#else +#elif defined(_MSC_VER) if (nIds >= 7) __cpuidex(cpuid_leaf_7,0x00000007,0); #endif #else diff --git a/thirdparty/embree/patches/mingw-no-cpuidex.patch b/thirdparty/embree/patches/mingw-no-cpuidex.patch new file mode 100644 index 0000000000..5480334ceb --- /dev/null +++ b/thirdparty/embree/patches/mingw-no-cpuidex.patch @@ -0,0 +1,13 @@ +diff --git a/thirdparty/embree/common/sys/sysinfo.cpp b/thirdparty/embree/common/sys/sysinfo.cpp +index d01eab3c9d..4ecab05265 100644 +--- a/thirdparty/embree/common/sys/sysinfo.cpp ++++ b/thirdparty/embree/common/sys/sysinfo.cpp +@@ -295,7 +295,7 @@ namespace embree + if (nIds >= 1) __cpuid (cpuid_leaf_1,0x00000001); + #if _WIN32 + #if _MSC_VER && (_MSC_FULL_VER < 160040219) +-#else ++#elif defined(_MSC_VER) + if (nIds >= 7) __cpuidex(cpuid_leaf_7,0x00000007,0); + #endif + #else diff --git a/thirdparty/glslang/glslang/Include/InfoSink.h b/thirdparty/glslang/glslang/Include/InfoSink.h index b1b537df54..137ede8510 100644 --- a/thirdparty/glslang/glslang/Include/InfoSink.h +++ b/thirdparty/glslang/glslang/Include/InfoSink.h @@ -36,7 +36,7 @@ #define _INFOSINK_INCLUDED_ #include "../Include/Common.h" -#include <filesystem> +//#include <filesystem> #include <cmath> namespace glslang { diff --git a/thirdparty/glslang/glslang/patches/disable-absolute-paths-for-apple-compat.patch b/thirdparty/glslang/patches/disable-absolute-paths-for-apple-compat.patch index d15a531b7c..135020737e 100644 --- a/thirdparty/glslang/glslang/patches/disable-absolute-paths-for-apple-compat.patch +++ b/thirdparty/glslang/patches/disable-absolute-paths-for-apple-compat.patch @@ -1,7 +1,16 @@ diff --git a/thirdparty/glslang/glslang/Include/InfoSink.h b/thirdparty/glslang/glslang/Include/InfoSink.h -index 23f495dcb7..b1b537df54 100644 +index 23f495dc..137ede85 100644 --- a/thirdparty/glslang/glslang/Include/InfoSink.h +++ b/thirdparty/glslang/glslang/Include/InfoSink.h +@@ -36,7 +36,7 @@ + #define _INFOSINK_INCLUDED_ + + #include "../Include/Common.h" +-#include <filesystem> ++//#include <filesystem> + #include <cmath> + + namespace glslang { @@ -101,14 +101,14 @@ public: snprintf(locText, maxSize, ":%d", loc.line); |