diff options
221 files changed, 4927 insertions, 3588 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 79fab50882..715ed61770 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1344,7 +1344,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/occlusion_culling/bvh_build_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), 2); GLOBAL_DEF(PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1"), 60); // No negative and limit to 500 due to crashes. GLOBAL_DEF_RST("internationalization/rendering/force_right_to_left_layout_direction", false); - GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_RANGE, "Based on Locale,Left-to-Right,Right-to-Left"), 0); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Locale,Left-to-Right,Right-to-Left"), 0); GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 2000); diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 6b8ec8d5f6..07677337b4 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -147,15 +147,28 @@ bool Resource::editor_can_reload_from_file() { return true; //by default yes } +void Resource::connect_changed(const Callable &p_callable, uint32_t p_flags) { + if (!is_connected(CoreStringNames::get_singleton()->changed, p_callable) || p_flags & CONNECT_REFERENCE_COUNTED) { + connect(CoreStringNames::get_singleton()->changed, p_callable, p_flags); + } +} + +void Resource::disconnect_changed(const Callable &p_callable) { + if (is_connected(CoreStringNames::get_singleton()->changed, p_callable)) { + disconnect(CoreStringNames::get_singleton()->changed, p_callable); + } +} + void Resource::reset_state() { } + Error Resource::copy_from(const Ref<Resource> &p_resource) { ERR_FAIL_COND_V(p_resource.is_null(), ERR_INVALID_PARAMETER); if (get_class() != p_resource->get_class()) { return ERR_INVALID_PARAMETER; } - reset_state(); //may want to reset state + reset_state(); // May want to reset state. List<PropertyInfo> pi; p_resource->get_property_list(&pi); @@ -322,23 +335,6 @@ RID Resource::get_rid() const { return RID(); } -void Resource::register_owner(Object *p_owner) { - owners.insert(p_owner->get_instance_id()); -} - -void Resource::unregister_owner(Object *p_owner) { - owners.erase(p_owner->get_instance_id()); -} - -void Resource::notify_change_to_owners() { - for (const ObjectID &E : owners) { - Object *obj = ObjectDB::get_instance(E); - ERR_CONTINUE_MSG(!obj, "Object was deleted, while still owning a resource."); //wtf - //TODO store string - obj->call("resource_changed", Ref<Resource>(this)); - } -} - #ifdef TOOLS_ENABLED uint32_t Resource::hash_edited_version() const { @@ -443,6 +439,7 @@ void Resource::_bind_methods() { ClassDB::bind_method(D_METHOD("is_local_to_scene"), &Resource::is_local_to_scene); ClassDB::bind_method(D_METHOD("get_local_scene"), &Resource::get_local_scene); ClassDB::bind_method(D_METHOD("setup_local_to_scene"), &Resource::setup_local_to_scene); + ClassDB::bind_method(D_METHOD("emit_changed"), &Resource::emit_changed); ClassDB::bind_method(D_METHOD("duplicate", "subresources"), &Resource::duplicate, DEFVAL(false)); @@ -469,9 +466,6 @@ Resource::~Resource() { ResourceCache::resources.erase(path_cache); ResourceCache::lock.unlock(); } - if (owners.size()) { - WARN_PRINT("Resource is still owned."); - } } HashMap<String, Resource *> ResourceCache::resources; diff --git a/core/io/resource.h b/core/io/resource.h index 5135664f36..af8c275a1c 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -54,8 +54,6 @@ public: virtual String get_base_extension() const { return "res"; } private: - HashSet<ObjectID> owners; - friend class ResBase; friend class ResourceCache; @@ -76,10 +74,6 @@ private: SelfList<Resource> remapped_list; protected: - void emit_changed(); - - void notify_change_to_owners(); - virtual void _resource_path_changed(); static void _bind_methods(); @@ -96,8 +90,9 @@ public: virtual Error copy_from(const Ref<Resource> &p_resource); virtual void reload_from_file(); - void register_owner(Object *p_owner); - void unregister_owner(Object *p_owner); + void emit_changed(); + void connect_changed(const Callable &p_callable, uint32_t p_flags = 0); + void disconnect_changed(const Callable &p_callable); void set_name(const String &p_name); String get_name() const; diff --git a/core/object/object.h b/core/object/object.h index a3e9d025ea..318dbf98de 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -430,6 +430,9 @@ protected: _FORCE_INLINE_ static void (*_get_bind_methods())() { \ return &m_class::_bind_methods; \ } \ + _FORCE_INLINE_ static void (*_get_bind_compatibility_methods())() { \ + return &m_class::_bind_compatibility_methods; \ + } \ \ public: \ static void initialize_class() { \ @@ -442,6 +445,9 @@ public: if (m_class::_get_bind_methods() != m_inherits::_get_bind_methods()) { \ _bind_methods(); \ } \ + if (m_class::_get_bind_compatibility_methods() != m_inherits::_get_bind_compatibility_methods()) { \ + _bind_compatibility_methods(); \ + } \ initialized = true; \ } \ \ @@ -674,6 +680,7 @@ protected: virtual void _notificationv(int p_notification, bool p_reversed) {} static void _bind_methods(); + static void _bind_compatibility_methods() {} bool _set(const StringName &p_name, const Variant &p_property) { return false; }; bool _get(const StringName &p_name, Variant &r_property) const { return false; }; void _get_property_list(List<PropertyInfo> *p_list) const {}; @@ -685,6 +692,9 @@ protected: _FORCE_INLINE_ static void (*_get_bind_methods())() { return &Object::_bind_methods; } + _FORCE_INLINE_ static void (*_get_bind_compatibility_methods())() { + return &Object::_bind_compatibility_methods; + } _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &r_ret) const { return &Object::_get; } diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 5215142dd3..5a0ded6c01 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -454,17 +454,21 @@ Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const { const int s = size(); - int begin = CLAMP(p_begin, -s, s); + if (s == 0 || (p_begin < -s && p_step < 0) || (p_begin >= s && p_step > 0)) { + return result; + } + + int begin = CLAMP(p_begin, -s, s - 1); if (begin < 0) { begin += s; } - int end = CLAMP(p_end, -s, s); + int end = CLAMP(p_end, -s - 1, s); if (end < 0) { end += s; } - ERR_FAIL_COND_V_MSG(p_step > 0 && begin > end, result, "Slice is positive, but bounds is decreasing."); - ERR_FAIL_COND_V_MSG(p_step < 0 && begin < end, result, "Slice is negative, but bounds is increasing."); + ERR_FAIL_COND_V_MSG(p_step > 0 && begin > end, result, "Slice step is positive, but bounds are decreasing."); + ERR_FAIL_COND_V_MSG(p_step < 0 && begin < end, result, "Slice step is negative, but bounds are increasing."); int result_size = (end - begin) / p_step + (((end - begin) % p_step != 0) ? 1 : 0); result.resize(result_size); diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index f62c44aa0d..999cc6fa29 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -584,6 +584,7 @@ If either [param begin] or [param end] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]). If specified, [param step] is the relative index between source elements. It can be negative, then [param begin] must be higher than [param end]. For example, [code][0, 1, 2, 3, 4, 5].slice(5, 1, -2)[/code] returns [code][5, 3][/code]. If [param deep] is true, each element will be copied by value rather than by reference. + [b]Note:[/b] To include the first element when [param step] is negative, use [code]arr.slice(begin, -arr.size() - 1, step)[/code] (i.e. [code][0, 1, 2].slice(1, -4, -1)[/code] returns [code][1, 0][/code]). </description> </method> <method name="sort"> diff --git a/doc/classes/CollisionShape3D.xml b/doc/classes/CollisionShape3D.xml index 171deb9c62..4e32545f27 100644 --- a/doc/classes/CollisionShape3D.xml +++ b/doc/classes/CollisionShape3D.xml @@ -20,11 +20,11 @@ Sets the collision shape's shape to the addition of all its convexed [MeshInstance3D] siblings geometry. </description> </method> - <method name="resource_changed"> + <method name="resource_changed" is_deprecated="true"> <return type="void" /> <param index="0" name="resource" type="Resource" /> <description> - If this method exists within a script it will be called whenever the shape resource has been modified. + [i]Obsoleted.[/i] Use [signal Resource.changed] instead. </description> </method> </methods> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 920fe901a8..9c075a979a 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -109,7 +109,7 @@ Displays OS native dialog for selecting files or directories in the file system. Callbacks have the following arguments: [code]bool status, PackedStringArray selected_paths[/code]. [b]Note:[/b] This method is implemented if the display server has the [code]FEATURE_NATIVE_DIALOG[/code] feature. - [b]Note:[/b] This method is implemented on macOS. + [b]Note:[/b] This method is implemented on Windows and macOS. [b]Note:[/b] On macOS, native file dialogs have no title. [b]Note:[/b] On macOS, sandboxed apps will save security-scoped bookmarks to retain access to the opened folders across multiple sessions. Use [method OS.get_granted_permissions] to get a list of saved bookmarks. </description> diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml index fa82447894..75641a4486 100644 --- a/doc/classes/EditorExportPlugin.xml +++ b/doc/classes/EditorExportPlugin.xml @@ -8,6 +8,7 @@ To use [EditorExportPlugin], register it using the [method EditorPlugin.add_export_plugin] method first. </description> <tutorials> + <link title="Export Android plugins">$DOCS_URL/tutorials/platform/android/android_plugin.html</link> </tutorials> <methods> <method name="_begin_customize_resources" qualifiers="virtual const"> @@ -84,6 +85,64 @@ Calling [method skip] inside this callback will make the file not included in the export. </description> </method> + <method name="_get_android_dependencies" qualifiers="virtual const"> + <return type="PackedStringArray" /> + <param index="0" name="platform" type="EditorExportPlatform" /> + <param index="1" name="debug" type="bool" /> + <description> + Virtual method to be overridden by the user. This is called to retrieve the set of Android dependencies provided by this plugin. Each returned Android dependency should have the format of an Android remote binary dependency: [code]org.godot.example:my-plugin:0.0.0[/code] + For more information see [url=https://developer.android.com/build/dependencies?agpversion=4.1#dependency-types]Android documentation on dependencies[/url]. + [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled. + </description> + </method> + <method name="_get_android_dependencies_maven_repos" qualifiers="virtual const"> + <return type="PackedStringArray" /> + <param index="0" name="platform" type="EditorExportPlatform" /> + <param index="1" name="debug" type="bool" /> + <description> + Virtual method to be overridden by the user. This is called to retrieve the URLs of Maven repositories for the set of Android dependencies provided by this plugin. + For more information see [url=https://docs.gradle.org/current/userguide/dependency_management.html#sec:maven_repo]Gradle documentation on dependency management[/url]. + [b]Note:[/b] Google's Maven repo and the Maven Central repo are already included by default. + [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled. + </description> + </method> + <method name="_get_android_libraries" qualifiers="virtual const"> + <return type="PackedStringArray" /> + <param index="0" name="platform" type="EditorExportPlatform" /> + <param index="1" name="debug" type="bool" /> + <description> + Virtual method to be overridden by the user. This is called to retrieve the local paths of the Android libraries archive (AAR) files provided by this plugin. + [b]Note:[/b] Relative paths **must** be relative to Godot's [code]res://addons/[/code] directory. For example, an AAR file located under [code]res://addons/hello_world_plugin/HelloWorld.release.aar[/code] can be returned as an absolute path using [code]res://addons/hello_world_plugin/HelloWorld.release.aar[/code] or a relative path using [code]hello_world_plugin/HelloWorld.release.aar[/code]. + [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled. + </description> + </method> + <method name="_get_android_manifest_activity_element_contents" qualifiers="virtual const"> + <return type="String" /> + <param index="0" name="platform" type="EditorExportPlatform" /> + <param index="1" name="debug" type="bool" /> + <description> + Virtual method to be overridden by the user. This is used at export time to update the contents of the [code]activity[/code] element in the generated Android manifest. + [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled. + </description> + </method> + <method name="_get_android_manifest_application_element_contents" qualifiers="virtual const"> + <return type="String" /> + <param index="0" name="platform" type="EditorExportPlatform" /> + <param index="1" name="debug" type="bool" /> + <description> + Virtual method to be overridden by the user. This is used at export time to update the contents of the [code]application[/code] element in the generated Android manifest. + [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled. + </description> + </method> + <method name="_get_android_manifest_element_contents" qualifiers="virtual const"> + <return type="String" /> + <param index="0" name="platform" type="EditorExportPlatform" /> + <param index="1" name="debug" type="bool" /> + <description> + Virtual method to be overridden by the user. This is used at export time to update the contents of the [code]manifest[/code] element in the generated Android manifest. + [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled. + </description> + </method> <method name="_get_customization_configuration_hash" qualifiers="virtual const"> <return type="int" /> <description> @@ -99,6 +158,15 @@ Return a [PackedStringArray] of additional features this preset, for the given [param platform], should have. </description> </method> + <method name="_get_export_option_warning" qualifiers="virtual const"> + <return type="String" /> + <param index="0" name="platform" type="EditorExportPlatform" /> + <param index="1" name="option" type="String" /> + <description> + Check the requirements for the given [param option] and return a non-empty warning string if they are not met. + [b]Note:[/b] Use [method get_option] to check the value of the export options. + </description> + </method> <method name="_get_export_options" qualifiers="virtual const"> <return type="Dictionary[]" /> <param index="0" name="platform" type="EditorExportPlatform" /> @@ -124,6 +192,13 @@ Return [code]true[/code], if the result of [method _get_export_options] has changed and the export options of preset corresponding to [param platform] should be updated. </description> </method> + <method name="_supports_platform" qualifiers="virtual const"> + <return type="bool" /> + <param index="0" name="platform" type="EditorExportPlatform" /> + <description> + Return [code]true[/code] if the plugin supports the given [param platform]. + </description> + </method> <method name="add_file"> <return type="void" /> <param index="0" name="path" type="String" /> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index 957b6d8e88..3429062328 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -280,6 +280,34 @@ [/codeblock] </description> </method> + <method name="_get_unsaved_status" qualifiers="virtual const"> + <return type="String" /> + <param index="0" name="for_scene" type="String" /> + <description> + Override this method to provide a custom message that lists unsaved changes. The editor will call this method when exiting or when closing a scene, and display the returned string in a confirmation dialog. Return empty string if the plugin has no unsaved changes. + When closing a scene, [param for_scene] is the path to the scene being closed. You can use it to handle built-in resources in that scene. + If the user confirms saving, [method _save_external_data] will be called, before closing the editor. + [codeblock] + func _get_unsaved_status(for_scene): + if not unsaved: + return "" + + if for_scene.is_empty(): + return "Save changes in MyCustomPlugin before closing?" + else: + return "Scene %s has changes from MyCustomPlugin. Save before closing?" % for_scene.get_file() + + func _save_external_data(): + unsaved = false + [/codeblock] + If the plugin has no scene-specific changes, you can ignore the calls when closing scenes: + [codeblock] + func _get_unsaved_status(for_scene): + if not for_scene.is_empty(): + return "" + [/codeblock] + </description> + </method> <method name="_get_window_layout" qualifiers="virtual"> <return type="void" /> <param index="0" name="configuration" type="ConfigFile" /> @@ -541,6 +569,12 @@ Returns the [PopupMenu] under [b]Scene > Export As...[/b]. </description> </method> + <method name="get_plugin_version" qualifiers="const"> + <return type="String" /> + <description> + Provide the version of the plugin declared in the [code]plugin.cfg[/code] config file. + </description> + </method> <method name="get_script_create_dialog"> <return type="ScriptCreateDialog" /> <description> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 744c72af4d..0c78525e45 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1943,13 +1943,13 @@ <member name="memory/limits/multithreaded_server/rid_pool_prealloc" type="int" setter="" getter="" default="60"> This is used by servers when used in multi-threading mode (servers and visual). RIDs are preallocated to avoid stalling the server requesting them on threads. If servers get stalled too often when loading resources in a thread, increase this number. </member> - <member name="navigation/2d/default_cell_size" type="int" setter="" getter="" default="1"> + <member name="navigation/2d/default_cell_size" type="float" setter="" getter="" default="1.0"> Default cell size for 2D navigation maps. See [method NavigationServer2D.map_set_cell_size]. </member> - <member name="navigation/2d/default_edge_connection_margin" type="int" setter="" getter="" default="1"> + <member name="navigation/2d/default_edge_connection_margin" type="float" setter="" getter="" default="1.0"> Default edge connection margin for 2D navigation maps. See [method NavigationServer2D.map_set_edge_connection_margin]. </member> - <member name="navigation/2d/default_link_connection_radius" type="int" setter="" getter="" default="4"> + <member name="navigation/2d/default_link_connection_radius" type="float" setter="" getter="" default="4.0"> Default link connection radius for 2D navigation maps. See [method NavigationServer2D.map_set_link_connection_radius]. </member> <member name="navigation/2d/use_edge_connections" type="bool" setter="" getter="" default="true"> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index ab993c372a..73507b10a7 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -16,8 +16,8 @@ <methods> <method name="barrier"> <return type="void" /> - <param index="0" name="from" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" /> - <param index="1" name="to" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" /> + <param index="0" name="from" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> + <param index="1" name="to" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> <description> Puts a memory barrier in place. This is used for synchronization to avoid data races. See also [method full_barrier], which may be useful for debugging. </description> @@ -27,7 +27,7 @@ <param index="0" name="buffer" type="RID" /> <param index="1" name="offset" type="int" /> <param index="2" name="size_bytes" type="int" /> - <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" /> + <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> <description> </description> </method> @@ -46,7 +46,7 @@ <param index="1" name="offset" type="int" /> <param index="2" name="size_bytes" type="int" /> <param index="3" name="data" type="PackedByteArray" /> - <param index="4" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" /> + <param index="4" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> <description> </description> </method> @@ -114,7 +114,7 @@ </method> <method name="compute_list_end"> <return type="void" /> - <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" /> + <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> <description> Finishes a list of compute commands created with the [code]compute_*[/code] methods. </description> @@ -296,7 +296,7 @@ </method> <method name="draw_list_end"> <return type="void" /> - <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" /> + <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> <description> Finishes a list of raster drawing commands created with the [code]draw_*[/code] methods. </description> @@ -682,7 +682,7 @@ <param index="3" name="mipmap_count" type="int" /> <param index="4" name="base_layer" type="int" /> <param index="5" name="layer_count" type="int" /> - <param index="6" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" /> + <param index="6" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> <description> Clears the specified [param texture] by replacing all of its pixels with the specified [param color]. [param base_mipmap] and [param mipmap_count] determine which mipmaps of the texture are affected by this clear operation, while [param base_layer] and [param layer_count] determine which layers of a 3D texture (or texture array) are affected by this clear operation. For 2D textures (which only have one layer by design), [param base_layer] and [param layer_count] must both be [code]0[/code]. [b]Note:[/b] [param texture] can't be cleared while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to clear this texture. @@ -699,7 +699,7 @@ <param index="6" name="dst_mipmap" type="int" /> <param index="7" name="src_layer" type="int" /> <param index="8" name="dst_layer" type="int" /> - <param index="9" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" /> + <param index="9" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> <description> Copies the [param from_texture] to [param to_texture] with the specified [param from_pos], [param to_pos] and [param size] coordinates. The Z axis of the [param from_pos], [param to_pos] and [param size] must be [code]0[/code] for 2-dimensional textures. Source and destination mipmaps/layers must also be specified, with these parameters being [code]0[/code] for textures without mipmaps or single-layer textures. Returns [constant @GlobalScope.OK] if the texture copy was successful or [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise. [b]Note:[/b] [param from_texture] texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to copy this texture. @@ -786,7 +786,7 @@ <return type="int" enum="Error" /> <param index="0" name="from_texture" type="RID" /> <param index="1" name="to_texture" type="RID" /> - <param index="2" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" /> + <param index="2" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> <description> Resolves the [param from_texture] texture onto [param to_texture] with multisample antialiasing enabled. This must be used when rendering a framebuffer for MSAA to work. Returns [constant @GlobalScope.OK] if successful, [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise. [b]Note:[/b] [param from_texture] and [param to_texture] textures must have the same dimension, format and type (color or depth). @@ -803,7 +803,7 @@ <param index="0" name="texture" type="RID" /> <param index="1" name="layer" type="int" /> <param index="2" name="data" type="PackedByteArray" /> - <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" /> + <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> <description> Updates texture data with new data, replacing the previous data in place. The updated texture data must have the same dimensions and format. For 2D textures (which only have one layer), [param layer] must be [code]0[/code]. Returns [constant @GlobalScope.OK] if the update was successful, [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise. [b]Note:[/b] Updating textures is forbidden during creation of a draw or compute list. @@ -1580,19 +1580,25 @@ <constant name="DATA_FORMAT_MAX" value="218" enum="DataFormat"> Represents the size of the [enum DataFormat] enum. </constant> - <constant name="BARRIER_MASK_RASTER" value="1" enum="BarrierMask" is_bitfield="true"> - Raster barrier mask. + <constant name="BARRIER_MASK_VERTEX" value="1" enum="BarrierMask" is_bitfield="true"> + Vertex shader barrier mask. </constant> - <constant name="BARRIER_MASK_COMPUTE" value="2" enum="BarrierMask" is_bitfield="true"> + <constant name="BARRIER_MASK_FRAGMENT" value="2" enum="BarrierMask" is_bitfield="true"> + Fragment shader barrier mask. + </constant> + <constant name="BARRIER_MASK_COMPUTE" value="4" enum="BarrierMask" is_bitfield="true"> Compute barrier mask. </constant> - <constant name="BARRIER_MASK_TRANSFER" value="4" enum="BarrierMask" is_bitfield="true"> + <constant name="BARRIER_MASK_TRANSFER" value="8" enum="BarrierMask" is_bitfield="true"> Transfer barrier mask. </constant> - <constant name="BARRIER_MASK_ALL_BARRIERS" value="7" enum="BarrierMask" is_bitfield="true"> + <constant name="BARRIER_MASK_RASTER" value="3" enum="BarrierMask" is_bitfield="true"> + Raster barrier mask (vertex and fragment). Equivalent to [code]BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT[/code]. + </constant> + <constant name="BARRIER_MASK_ALL_BARRIERS" value="15" enum="BarrierMask" is_bitfield="true"> Barrier mask for all types (raster, compute, transfer). Equivalent to [code]BARRIER_MASK_RASTER | BARRIER_MASK_COMPUTE | BARRIER_MASK_TRANSFER[/code]. </constant> - <constant name="BARRIER_MASK_NO_BARRIER" value="8" enum="BarrierMask" is_bitfield="true"> + <constant name="BARRIER_MASK_NO_BARRIER" value="16" enum="BarrierMask" is_bitfield="true"> No barrier for any type. </constant> <constant name="TEXTURE_TYPE_1D" value="0" enum="TextureType"> diff --git a/doc/classes/ShapeCast3D.xml b/doc/classes/ShapeCast3D.xml index 4bbf763a6e..ce72944098 100644 --- a/doc/classes/ShapeCast3D.xml +++ b/doc/classes/ShapeCast3D.xml @@ -119,11 +119,11 @@ Removes a collision exception so the shape does report collisions with the specified [RID]. </description> </method> - <method name="resource_changed"> + <method name="resource_changed" is_deprecated="true"> <return type="void" /> <param index="0" name="resource" type="Resource" /> <description> - This method is used internally to update the debug gizmo in the editor. Any code placed in this function will be called whenever the [member shape] resource is modified. + [i]Obsoleted.[/i] Use [signal Resource.changed] instead. </description> </method> <method name="set_collision_mask_value"> diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index b46af012f1..a8074aa3f6 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_posix.cpp @@ -204,6 +204,9 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { if (err == WSAEACCES) { return ERR_NET_UNAUTHORIZED; } + if (err == WSAEMSGSIZE || err == WSAENOBUFS) { + return ERR_NET_BUFFER_TOO_SMALL; + } print_verbose("Socket error: " + itos(err)); return ERR_NET_OTHER; #else @@ -222,6 +225,9 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { if (errno == EACCES) { return ERR_NET_UNAUTHORIZED; } + if (errno == ENOBUFS) { + return ERR_NET_BUFFER_TOO_SMALL; + } print_verbose("Socket error: " + itos(errno)); return ERR_NET_OTHER; #endif @@ -550,6 +556,10 @@ Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) { return ERR_BUSY; } + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } + return FAILED; } @@ -571,6 +581,10 @@ Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddr return ERR_BUSY; } + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } + return FAILED; } @@ -606,6 +620,9 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) { if (err == ERR_NET_WOULD_BLOCK) { return ERR_BUSY; } + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } return FAILED; } @@ -625,6 +642,9 @@ Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP if (err == ERR_NET_WOULD_BLOCK) { return ERR_BUSY; } + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } return FAILED; } diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_posix.h index bd2088b4f9..2682530e15 100644 --- a/drivers/unix/net_socket_posix.h +++ b/drivers/unix/net_socket_posix.h @@ -56,6 +56,7 @@ private: ERR_NET_IN_PROGRESS, ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE, ERR_NET_UNAUTHORIZED, + ERR_NET_BUFFER_TOO_SMALL, ERR_NET_OTHER, }; diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 23c6919854..6cf8f2dfac 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -54,9 +54,13 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; r_access_mask |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; if (buffer->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) { - if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) { + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + r_stage_mask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; @@ -68,8 +72,11 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID r_access_mask |= VK_ACCESS_INDEX_READ_BIT; buffer = index_buffer_owner.get_or_null(p_buffer); } else if (uniform_buffer_owner.owns(p_buffer)) { - if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) { - r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + r_stage_mask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { r_stage_mask |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; @@ -77,8 +84,12 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID r_access_mask |= VK_ACCESS_UNIFORM_READ_BIT; buffer = uniform_buffer_owner.get_or_null(p_buffer); } else if (texture_buffer_owner.owns(p_buffer)) { - if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) { - r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + r_access_mask |= VK_ACCESS_SHADER_READ_BIT; + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + r_stage_mask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; r_access_mask |= VK_ACCESS_SHADER_READ_BIT; } if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { @@ -89,8 +100,12 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID buffer = &texture_buffer_owner.get_or_null(p_buffer)->buffer; } else if (storage_buffer_owner.owns(p_buffer)) { buffer = storage_buffer_owner.get_or_null(p_buffer); - if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) { - r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + r_stage_mask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { @@ -2625,8 +2640,12 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } - if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { @@ -3020,8 +3039,12 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } - if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { @@ -3198,8 +3221,12 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } - if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { @@ -3334,8 +3361,12 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } - if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { @@ -7651,10 +7682,14 @@ void RenderingDeviceVulkan::draw_list_end(BitField<BarrierMask> p_post_barrier) barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } - if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT /*| VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT*/; + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT /*| VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT*/; access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT /*| VK_ACCESS_INDIRECT_COMMAND_READ_BIT*/; } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT /*| VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT*/; + access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT /*| VK_ACCESS_INDIRECT_COMMAND_READ_BIT*/; + } if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; @@ -8199,10 +8234,14 @@ void RenderingDeviceVulkan::compute_list_end(BitField<BarrierMask> p_post_barrie barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } - if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; + if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { + barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT; } + if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { + barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; + access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + } if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; @@ -8227,7 +8266,7 @@ void RenderingDeviceVulkan::barrier(BitField<BarrierMask> p_from, BitField<Barri src_barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; src_access_flags |= VK_ACCESS_SHADER_WRITE_BIT; } - if (p_from.has_flag(BARRIER_MASK_RASTER)) { + if (p_from.has_flag(BARRIER_MASK_FRAGMENT)) { src_barrier_flags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; src_access_flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; } @@ -8247,10 +8286,14 @@ void RenderingDeviceVulkan::barrier(BitField<BarrierMask> p_from, BitField<Barri dst_barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; dst_access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } - if (p_to.has_flag(BARRIER_MASK_RASTER)) { - dst_barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; + if (p_to.has_flag(BARRIER_MASK_VERTEX)) { + dst_barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; dst_access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT; } + if (p_to.has_flag(BARRIER_MASK_FRAGMENT)) { + dst_barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; + dst_access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + } if (p_to.has_flag(BARRIER_MASK_TRANSFER)) { dst_barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; dst_access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 24a6d89118..69728133f8 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -3297,7 +3297,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re track_edits[_get_track_selected()]->release_focus(); } if (animation.is_valid()) { - animation->disconnect("changed", callable_mp(this, &AnimationTrackEditor::_animation_changed)); + animation->disconnect_changed(callable_mp(this, &AnimationTrackEditor::_animation_changed)); _clear_selection(); } animation = p_anim; @@ -3308,7 +3308,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re _update_tracks(); if (animation.is_valid()) { - animation->connect("changed", callable_mp(this, &AnimationTrackEditor::_animation_changed)); + animation->connect_changed(callable_mp(this, &AnimationTrackEditor::_animation_changed)); hscroll->show(); edit->set_disabled(read_only); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 367373dc17..d95b1de365 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -45,6 +45,7 @@ #include "scene/gui/texture_rect.h" #include "scene/property_utils.h" #include "scene/resources/packed_scene.h" +#include "scene/resources/style_box_flat.h" bool EditorInspector::_property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) { if (p_property_path.findn(p_filter) != -1) { diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index a5737b7dc6..9a4c4f7f99 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -44,6 +44,7 @@ class OptionButton; class PanelContainer; class PopupMenu; class SpinBox; +class StyleBoxFlat; class TextureRect; class EditorPropertyRevert { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 0bdbf29ee1..5029a12264 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2777,6 +2777,11 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { case FILE_QUIT: case RUN_PROJECT_MANAGER: case RELOAD_CURRENT_PROJECT: { + if (p_confirmed && plugin_to_save) { + plugin_to_save->save_external_data(); + p_confirmed = false; + } + if (!p_confirmed) { bool save_each = EDITOR_GET("interface/editor/save_each_scene_on_quit"); if (_next_unsaved_scene(!save_each) == -1) { @@ -2793,6 +2798,28 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { break; } + plugin_to_save = nullptr; + for (int i = 0; i < editor_data.get_editor_plugin_count(); i++) { + const String unsaved_status = editor_data.get_editor_plugin(i)->get_unsaved_status(); + if (!unsaved_status.is_empty()) { + if (p_option == RELOAD_CURRENT_PROJECT) { + save_confirmation->set_ok_button_text(TTR("Save & Reload")); + save_confirmation->set_text(unsaved_status); + } else { + save_confirmation->set_ok_button_text(TTR("Save & Quit")); + save_confirmation->set_text(unsaved_status); + } + save_confirmation->reset_size(); + save_confirmation->popup_centered(); + plugin_to_save = editor_data.get_editor_plugin(i); + break; + } + } + + if (plugin_to_save) { + break; + } + _discard_changes(); break; } @@ -3031,13 +3058,21 @@ int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) { if (!editor_data.get_edited_scene_root(i)) { continue; } + + String scene_filename = editor_data.get_edited_scene_root(i)->get_scene_file_path(); + if (p_valid_filename && scene_filename.is_empty()) { + continue; + } + bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(i)); if (unsaved) { - String scene_filename = editor_data.get_edited_scene_root(i)->get_scene_file_path(); - if (p_valid_filename && scene_filename.is_empty()) { - continue; - } return i; + } else { + for (int j = 0; j < editor_data.get_editor_plugin_count(); j++) { + if (!editor_data.get_editor_plugin(j)->get_unsaved_status(scene_filename).is_empty()) { + return i; + } + } } } return -1; @@ -3192,7 +3227,7 @@ void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed if (icon.is_valid()) { tb->set_icon(icon); // Make sure the control is updated if the icon is reimported. - icon->connect("changed", callable_mp((Control *)tb, &Control::update_minimum_size)); + icon->connect_changed(callable_mp((Control *)tb, &Control::update_minimum_size)); } else if (singleton->gui_base->has_theme_icon(p_editor->get_name(), SNAME("EditorIcons"))) { tb->set_icon(singleton->gui_base->get_theme_icon(p_editor->get_name(), SNAME("EditorIcons"))); } @@ -3328,6 +3363,11 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, return; } + String plugin_version; + if (cf->has_section_key("plugin", "version")) { + plugin_version = cf->get_value("plugin", "version"); + } + if (!cf->has_section_key("plugin", "script")) { show_warning(vformat(TTR("Unable to find script field for addon plugin at: '%s'."), addon_path)); return; @@ -3373,6 +3413,7 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, EditorPlugin *ep = memnew(EditorPlugin); ep->set_script(scr); + ep->set_plugin_version(plugin_version); addon_name_to_plugin[addon_path] = ep; add_editor_plugin(ep, p_config_changed); @@ -4409,17 +4450,18 @@ String EditorNode::_get_system_info() const { const int processor_count = OS::get_singleton()->get_processor_count(); // Prettify - if (driver_name == "vulkan") { - driver_name = "Vulkan"; - } else if (driver_name == "opengl3") { - driver_name = "GLES3"; - } if (rendering_method == "forward_plus") { rendering_method = "Forward+"; } else if (rendering_method == "mobile") { rendering_method = "Mobile"; } else if (rendering_method == "gl_compatibility") { rendering_method = "Compatibility"; + driver_name = GLOBAL_GET("rendering/gl_compatibility/driver"); + } + if (driver_name == "vulkan") { + driver_name = "Vulkan"; + } else if (driver_name == "opengl3") { + driver_name = "GLES3"; } // Join info. @@ -5547,19 +5589,36 @@ void EditorNode::_scene_tab_closed(int p_tab, int p_option) { return; } - bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(p_tab)); - if (unsaved) { + String scene_filename = scene->get_scene_file_path(); + String unsaved_message; + + if (EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(p_tab))) { + if (scene_filename.is_empty()) { + unsaved_message = TTR("This scene was never saved."); + } else { + unsaved_message = vformat(TTR("Scene \"%s\" has unsaved changes."), scene_filename); + } + } else { + // Check if any plugin has unsaved changes in that scene. + for (int i = 0; i < editor_data.get_editor_plugin_count(); i++) { + unsaved_message = editor_data.get_editor_plugin(i)->get_unsaved_status(scene_filename); + if (!unsaved_message.is_empty()) { + break; + } + } + } + + if (!unsaved_message.is_empty()) { if (get_current_tab() != p_tab) { set_current_scene(p_tab); } - String scene_filename = scene->get_scene_file_path(); if (current_menu_option == RELOAD_CURRENT_PROJECT) { save_confirmation->set_ok_button_text(TTR("Save & Reload")); - save_confirmation->set_text(vformat(TTR("Save changes to '%s' before reloading?"), !scene_filename.is_empty() ? scene_filename : "unsaved scene")); + save_confirmation->set_text(unsaved_message + "\n\n" + TTR("Save before reloading?")); } else { save_confirmation->set_ok_button_text(TTR("Save & Close")); - save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), !scene_filename.is_empty() ? scene_filename : "unsaved scene")); + save_confirmation->set_text(unsaved_message + "\n\n" + TTR("Save before closing?")); } save_confirmation->reset_size(); save_confirmation->popup_centered(); diff --git a/editor/editor_node.h b/editor/editor_node.h index 65f85a76c9..6384c27c72 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -382,6 +382,7 @@ private: AcceptDialog *save_accept = nullptr; EditorAbout *about = nullptr; AcceptDialog *warning = nullptr; + EditorPlugin *plugin_to_save = nullptr; int overridden_default_layout = -1; Ref<ConfigFile> default_layout; diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 5170c2fdfb..2d4c07b263 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -307,6 +307,14 @@ const Ref<Texture2D> EditorPlugin::get_icon() const { return icon; } +String EditorPlugin::get_plugin_version() const { + return plugin_version; +} + +void EditorPlugin::set_plugin_version(const String &p_version) { + plugin_version = p_version; +} + bool EditorPlugin::has_main_screen() const { bool success = false; GDVIRTUAL_CALL(_has_main_screen, success); @@ -341,7 +349,12 @@ void EditorPlugin::clear() { GDVIRTUAL_CALL(_clear); } -// if editor references external resources/scenes, save them +String EditorPlugin::get_unsaved_status(const String &p_for_scene) const { + String ret; + GDVIRTUAL_CALL(_get_unsaved_status, p_for_scene, ret); + return ret; +} + void EditorPlugin::save_external_data() { GDVIRTUAL_CALL(_save_external_data); } @@ -578,6 +591,7 @@ void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog); ClassDB::bind_method(D_METHOD("add_debugger_plugin", "script"), &EditorPlugin::add_debugger_plugin); ClassDB::bind_method(D_METHOD("remove_debugger_plugin", "script"), &EditorPlugin::remove_debugger_plugin); + ClassDB::bind_method(D_METHOD("get_plugin_version"), &EditorPlugin::get_plugin_version); GDVIRTUAL_BIND(_forward_canvas_gui_input, "event"); GDVIRTUAL_BIND(_forward_canvas_draw_over_viewport, "viewport_control"); @@ -594,6 +608,7 @@ void EditorPlugin::_bind_methods() { GDVIRTUAL_BIND(_get_state); GDVIRTUAL_BIND(_set_state, "state"); GDVIRTUAL_BIND(_clear); + GDVIRTUAL_BIND(_get_unsaved_status, "for_scene"); GDVIRTUAL_BIND(_save_external_data); GDVIRTUAL_BIND(_apply_changes); GDVIRTUAL_BIND(_get_breakpoints); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 69789a4d4f..7dcf62144d 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -61,6 +61,7 @@ class EditorPlugin : public Node { bool force_draw_over_forwarding_enabled = false; String last_main_screen_name; + String plugin_version; void _editor_project_settings_changed(); @@ -88,6 +89,7 @@ protected: GDVIRTUAL0RC(Dictionary, _get_state) GDVIRTUAL1(_set_state, Dictionary) GDVIRTUAL0(_clear) + GDVIRTUAL1RC(String, _get_unsaved_status, String) GDVIRTUAL0(_save_external_data) GDVIRTUAL0(_apply_changes) GDVIRTUAL0RC(Vector<String>, _get_breakpoints) @@ -167,6 +169,8 @@ public: virtual String get_name() const; virtual const Ref<Texture2D> get_icon() const; + virtual String get_plugin_version() const; + virtual void set_plugin_version(const String &p_version); virtual bool has_main_screen() const; virtual void make_visible(bool p_visible); virtual void selected_notify() {} //notify that it was raised by the user, not the editor @@ -175,6 +179,7 @@ public: virtual Dictionary get_state() const; //save editor state so it can't be reloaded when reloading scene virtual void set_state(const Dictionary &p_state); //restore editor state (likely was saved with the scene) virtual void clear(); // clear any temporary data in the editor, reset it (likely new scene or load another scene) + virtual String get_unsaved_status(const String &p_for_scene = "") const; virtual void save_external_data(); // if editor references external resources/scenes, save them virtual void apply_changes(); // if changes are pending in editor, apply them virtual void get_breakpoints(List<String> *p_breakpoints); diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 15cf96b928..ea7e1549f5 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -41,6 +41,8 @@ #include "editor/plugins/editor_resource_conversion_plugin.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/scene_tree_dock.h" +#include "scene/gui/button.h" +#include "scene/gui/texture_rect.h" #include "scene/resources/gradient_texture.h" #include "scene/resources/image_texture.h" @@ -220,7 +222,7 @@ void EditorResourcePicker::_update_menu_items() { edited_resource->get_property_list(&property_list); bool has_subresources = false; for (PropertyInfo &p : property_list) { - if ((p.type == Variant::OBJECT) && (p.hint == PROPERTY_HINT_RESOURCE_TYPE) && (p.name != "script")) { + if ((p.type == Variant::OBJECT) && (p.hint == PROPERTY_HINT_RESOURCE_TYPE) && (p.name != "script") && ((Object *)edited_resource->get(p.name) != nullptr)) { has_subresources = true; break; } @@ -352,7 +354,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { } Ref<Resource> unique_resource = edited_resource->duplicate(); - ERR_FAIL_COND(unique_resource.is_null()); + ERR_FAIL_COND(unique_resource.is_null()); // duplicate() may fail. edited_resource = unique_resource; emit_signal(SNAME("resource_changed"), edited_resource); @@ -364,12 +366,30 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { return; } - Ref<Resource> unique_resource = edited_resource->duplicate(true); - ERR_FAIL_COND(unique_resource.is_null()); + if (!duplicate_resources_dialog) { + duplicate_resources_dialog = memnew(ConfirmationDialog); + add_child(duplicate_resources_dialog); + duplicate_resources_dialog->set_title(TTR("Make Unique (Recursive)")); + duplicate_resources_dialog->connect("confirmed", callable_mp(this, &EditorResourcePicker::_duplicate_selected_resources)); - edited_resource = unique_resource; - emit_signal(SNAME("resource_changed"), edited_resource); - _update_resource(); + VBoxContainer *vb = memnew(VBoxContainer); + duplicate_resources_dialog->add_child(vb); + + Label *label = memnew(Label(TTR("Select resources to make unique:"))); + vb->add_child(label); + + duplicate_resources_tree = memnew(Tree); + vb->add_child(duplicate_resources_tree); + duplicate_resources_tree->set_columns(2); + duplicate_resources_tree->set_v_size_flags(SIZE_EXPAND_FILL); + } + + duplicate_resources_tree->clear(); + TreeItem *root = duplicate_resources_tree->create_item(); + _gather_resources_to_duplicate(edited_resource, root); + + duplicate_resources_dialog->reset_size(); + duplicate_resources_dialog->popup_centered(Vector2(500, 400) * EDSCALE); } break; case OBJ_MENU_SAVE: { @@ -810,6 +830,11 @@ void EditorResourcePicker::_notification(int p_what) { } } +void EditorResourcePicker::set_assign_button_min_size(const Size2i &p_size) { + assign_button_min_size = p_size; + assign_button->set_custom_minimum_size(assign_button_min_size); +} + void EditorResourcePicker::set_base_type(const String &p_base_type) { base_type = p_base_type; @@ -922,6 +947,89 @@ void EditorResourcePicker::_ensure_resource_menu() { edit_menu->connect("popup_hide", callable_mp((BaseButton *)edit_button, &BaseButton::set_pressed).bind(false)); } +void EditorResourcePicker::_gather_resources_to_duplicate(const Ref<Resource> p_resource, TreeItem *p_item, const String &p_property_name) const { + p_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + + String res_name = p_resource->get_name(); + if (res_name.is_empty() && !p_resource->is_built_in()) { + res_name = p_resource->get_path().get_file(); + } + + if (res_name.is_empty()) { + p_item->set_text(0, p_resource->get_class()); + } else { + p_item->set_text(0, vformat("%s (%s)", p_resource->get_class(), res_name)); + } + + p_item->set_icon(0, EditorNode::get_singleton()->get_object_icon(p_resource.ptr())); + p_item->set_editable(0, true); + + Array meta; + meta.append(p_resource); + p_item->set_metadata(0, meta); + + if (!p_property_name.is_empty()) { + p_item->set_text(1, p_property_name); + } + + static Vector<String> unique_exceptions = { "Image", "Shader", "Mesh", "FontFile" }; + if (!unique_exceptions.has(p_resource->get_class())) { + // Automatically select resource, unless it's something that shouldn't be duplicated. + p_item->set_checked(0, true); + } + + List<PropertyInfo> plist; + p_resource->get_property_list(&plist); + + for (const PropertyInfo &E : plist) { + if (!(E.usage & PROPERTY_USAGE_STORAGE) || E.type != Variant::OBJECT || E.hint != PROPERTY_HINT_RESOURCE_TYPE) { + continue; + } + + Ref<Resource> res = p_resource->get(E.name); + if (res.is_null()) { + continue; + } + + TreeItem *child = p_item->create_child(); + _gather_resources_to_duplicate(res, child, E.name); + + meta = child->get_metadata(0); + // Remember property name. + meta.append(E.name); + + if ((E.usage & PROPERTY_USAGE_NEVER_DUPLICATE)) { + // The resource can't be duplicated, but make it appear on the list anyway. + child->set_checked(0, false); + child->set_editable(0, false); + } + } +} + +void EditorResourcePicker::_duplicate_selected_resources() { + for (TreeItem *item = duplicate_resources_tree->get_root(); item; item = item->get_next_in_tree()) { + if (!item->is_checked(0)) { + continue; + } + + Array meta = item->get_metadata(0); + Ref<Resource> res = meta[0]; + Ref<Resource> unique_resource = res->duplicate(); + ERR_FAIL_COND(unique_resource.is_null()); // duplicate() may fail. + meta[0] = unique_resource; + + if (meta.size() == 1) { // Root. + edited_resource = unique_resource; + emit_signal(SNAME("resource_changed"), edited_resource); + _update_resource(); + } else { + Array parent_meta = item->get_parent()->get_metadata(0); + Ref<Resource> parent = parent_meta[0]; + parent->set(meta[1], unique_resource); + } + } +} + EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) { assign_button = memnew(Button); assign_button->set_flat(true); diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index a302e24957..856ef974d3 100644 --- a/editor/editor_resource_picker.h +++ b/editor/editor_resource_picker.h @@ -32,12 +32,15 @@ #define EDITOR_RESOURCE_PICKER_H #include "scene/gui/box_container.h" -#include "scene/gui/button.h" -#include "scene/gui/popup_menu.h" -#include "scene/gui/texture_rect.h" +class Button; +class ConfirmationDialog; class EditorFileDialog; class EditorQuickOpen; +class PopupMenu; +class TextureRect; +class Tree; +class TreeItem; class EditorResourcePicker : public HBoxContainer { GDCLASS(EditorResourcePicker, HBoxContainer); @@ -56,6 +59,9 @@ class EditorResourcePicker : public HBoxContainer { EditorFileDialog *file_dialog = nullptr; EditorQuickOpen *quick_open = nullptr; + ConfirmationDialog *duplicate_resources_dialog = nullptr; + Tree *duplicate_resources_tree = nullptr; + Size2i assign_button_min_size = Size2i(1, 1); enum MenuOption { @@ -99,6 +105,8 @@ class EditorResourcePicker : public HBoxContainer { void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); void _ensure_resource_menu(); + void _gather_resources_to_duplicate(const Ref<Resource> p_resource, TreeItem *p_item, const String &p_property_name = "") const; + void _duplicate_selected_resources(); protected: virtual void _update_resource(); @@ -107,10 +115,7 @@ protected: static void _bind_methods(); void _notification(int p_what); - void set_assign_button_min_size(const Size2i &p_size) { - assign_button_min_size = p_size; - assign_button->set_custom_minimum_size(assign_button_min_size); - } + void set_assign_button_min_size(const Size2i &p_size); GDVIRTUAL1(_set_create_options, Object *) GDVIRTUAL1R(bool, _handle_menu_selected, int) diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 92c111a418..99feee42b8 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -37,6 +37,9 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "scene/resources/image_texture.h" +#include "scene/resources/style_box_flat.h" +#include "scene/resources/style_box_line.h" +#include "scene/resources/style_box_texture.h" #include "modules/modules_enabled.gen.h" // For svg. #ifdef MODULE_SVG_ENABLED diff --git a/editor/editor_undo_redo_manager.cpp b/editor/editor_undo_redo_manager.cpp index fd2d51be32..abfbd5e7c0 100644 --- a/editor/editor_undo_redo_manager.cpp +++ b/editor/editor_undo_redo_manager.cpp @@ -264,6 +264,7 @@ void EditorUndoRedoManager::commit_action(bool p_execute) { pending_action.action_name == prev_action.action_name && pending_action.action_name == pre_prev_action.action_name) { pending_action = Action(); is_committing = false; + emit_signal(SNAME("history_changed")); return; } } break; @@ -272,6 +273,7 @@ void EditorUndoRedoManager::commit_action(bool p_execute) { if (pending_action.merge_mode == prev_action.merge_mode && pending_action.action_name == prev_action.action_name) { pending_action = Action(); is_committing = false; + emit_signal(SNAME("history_changed")); return; } } break; diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 79a27bd337..5ee9b187a2 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -990,7 +990,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & struct SortByName { bool operator()(const Ref<EditorExportPlugin> &left, const Ref<EditorExportPlugin> &right) const { - return left->_get_name() < right->_get_name(); + return left->get_name() < right->get_name(); } }; @@ -1033,14 +1033,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & if (export_plugins.write[i]->_begin_customize_resources(Ref<EditorExportPlatform>(this), features_psa)) { customize_resources_plugins.push_back(export_plugins[i]); - custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->_get_name().hash64(), custom_resources_hash); + custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->get_name().hash64(), custom_resources_hash); uint64_t hash = export_plugins[i]->_get_customization_configuration_hash(); custom_resources_hash = hash_murmur3_one_64(hash, custom_resources_hash); } if (export_plugins.write[i]->_begin_customize_scenes(Ref<EditorExportPlatform>(this), features_psa)) { customize_scenes_plugins.push_back(export_plugins[i]); - custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->_get_name().hash64(), custom_resources_hash); + custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->get_name().hash64(), custom_resources_hash); uint64_t hash = export_plugins[i]->_get_customization_configuration_hash(); custom_scene_hash = hash_murmur3_one_64(hash, custom_scene_hash); } @@ -1800,6 +1800,24 @@ bool EditorExportPlatform::can_export(const Ref<EditorExportPreset> &p_preset, S if (!templates_error.is_empty()) { r_error += templates_error; } + + String export_plugins_warning; + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + Ref<EditorExportPlatform> export_platform = Ref<EditorExportPlatform>(this); + if (!export_plugins[i]->supports_platform(export_platform)) { + continue; + } + + String plugin_warning = export_plugins.write[i]->_has_valid_export_configuration(export_platform, p_preset); + if (!plugin_warning.is_empty()) { + export_plugins_warning += plugin_warning; + } + } + + if (!export_plugins_warning.is_empty()) { + r_error += export_plugins_warning; + } #endif String project_configuration_error; diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp index 4e2c1a9af7..6576960b9a 100644 --- a/editor/export/editor_export_plugin.cpp +++ b/editor/export/editor_export_plugin.cpp @@ -132,6 +132,27 @@ Variant EditorExportPlugin::get_option(const StringName &p_name) const { return export_preset->get(p_name); } +String EditorExportPlugin::_has_valid_export_configuration(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset) { + String warning; + if (!supports_platform(p_export_platform)) { + warning += vformat(TTR("Plugin \"%s\" is not supported on \"%s\""), get_name(), p_export_platform->get_name()); + warning += "\n"; + return warning; + } + + set_export_preset(p_preset); + List<EditorExportPlatform::ExportOption> options; + _get_export_options(p_export_platform, &options); + for (const EditorExportPlatform::ExportOption &E : options) { + String option_warning = _get_export_option_warning(p_export_platform, E.option.name); + if (!option_warning.is_empty()) { + warning += option_warning + "\n"; + } + } + + return warning; +} + void EditorExportPlugin::_export_file_script(const String &p_path, const String &p_type, const Vector<String> &p_features) { GDVIRTUAL_CALL(_export_file, p_path, p_type, p_features); } @@ -184,12 +205,54 @@ void EditorExportPlugin::_end_customize_resources() { GDVIRTUAL_CALL(_end_customize_resources); } -String EditorExportPlugin::_get_name() const { +String EditorExportPlugin::get_name() const { String ret; GDVIRTUAL_REQUIRED_CALL(_get_name, ret); return ret; } +bool EditorExportPlugin::supports_platform(const Ref<EditorExportPlatform> &p_export_platform) const { + bool ret = false; + GDVIRTUAL_CALL(_supports_platform, p_export_platform, ret); + return ret; +} + +PackedStringArray EditorExportPlugin::get_android_dependencies(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const { + PackedStringArray ret; + GDVIRTUAL_CALL(_get_android_dependencies, p_export_platform, p_debug, ret); + return ret; +} + +PackedStringArray EditorExportPlugin::get_android_dependencies_maven_repos(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const { + PackedStringArray ret; + GDVIRTUAL_CALL(_get_android_dependencies_maven_repos, p_export_platform, p_debug, ret); + return ret; +} + +PackedStringArray EditorExportPlugin::get_android_libraries(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const { + PackedStringArray ret; + GDVIRTUAL_CALL(_get_android_libraries, p_export_platform, p_debug, ret); + return ret; +} + +String EditorExportPlugin::get_android_manifest_activity_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const { + String ret; + GDVIRTUAL_CALL(_get_android_manifest_activity_element_contents, p_export_platform, p_debug, ret); + return ret; +} + +String EditorExportPlugin::get_android_manifest_application_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const { + String ret; + GDVIRTUAL_CALL(_get_android_manifest_application_element_contents, p_export_platform, p_debug, ret); + return ret; +} + +String EditorExportPlugin::get_android_manifest_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const { + String ret; + GDVIRTUAL_CALL(_get_android_manifest_element_contents, p_export_platform, p_debug, ret); + return ret; +} + PackedStringArray EditorExportPlugin::_get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const { PackedStringArray ret; GDVIRTUAL_CALL(_get_export_features, p_platform, p_debug, ret); @@ -216,6 +279,12 @@ bool EditorExportPlugin::_should_update_export_options(const Ref<EditorExportPla return ret; } +String EditorExportPlugin::_get_export_option_warning(const Ref<EditorExportPlatform> &p_export_platform, const String &p_option_name) const { + String ret; + GDVIRTUAL_CALL(_get_export_option_warning, p_export_platform, p_option_name, ret); + return ret; +} + void EditorExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) { } @@ -257,9 +326,19 @@ void EditorExportPlugin::_bind_methods() { GDVIRTUAL_BIND(_get_export_options, "platform"); GDVIRTUAL_BIND(_should_update_export_options, "platform"); + GDVIRTUAL_BIND(_get_export_option_warning, "platform", "option"); GDVIRTUAL_BIND(_get_export_features, "platform", "debug"); GDVIRTUAL_BIND(_get_name); + + GDVIRTUAL_BIND(_supports_platform, "platform"); + + GDVIRTUAL_BIND(_get_android_dependencies, "platform", "debug"); + GDVIRTUAL_BIND(_get_android_dependencies_maven_repos, "platform", "debug"); + GDVIRTUAL_BIND(_get_android_libraries, "platform", "debug"); + GDVIRTUAL_BIND(_get_android_manifest_activity_element_contents, "platform", "debug"); + GDVIRTUAL_BIND(_get_android_manifest_application_element_contents, "platform", "debug"); + GDVIRTUAL_BIND(_get_android_manifest_element_contents, "platform", "debug"); } EditorExportPlugin::EditorExportPlugin() { diff --git a/editor/export/editor_export_plugin.h b/editor/export/editor_export_plugin.h index 120141b347..7d866ce37e 100644 --- a/editor/export/editor_export_plugin.h +++ b/editor/export/editor_export_plugin.h @@ -42,6 +42,7 @@ class EditorExportPlugin : public RefCounted { friend class EditorExport; friend class EditorExportPlatform; + friend class EditorExportPreset; Ref<EditorExportPreset> export_preset; @@ -85,6 +86,8 @@ class EditorExportPlugin : public RefCounted { void _export_begin_script(const Vector<String> &p_features, bool p_debug, const String &p_path, int p_flags); void _export_end_script(); + String _has_valid_export_configuration(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset); + protected: void set_export_preset(const Ref<EditorExportPreset> &p_preset); Ref<EditorExportPreset> get_export_preset() const; @@ -125,9 +128,19 @@ protected: GDVIRTUAL2RC(PackedStringArray, _get_export_features, const Ref<EditorExportPlatform> &, bool); GDVIRTUAL1RC(TypedArray<Dictionary>, _get_export_options, const Ref<EditorExportPlatform> &); GDVIRTUAL1RC(bool, _should_update_export_options, const Ref<EditorExportPlatform> &); + GDVIRTUAL2RC(String, _get_export_option_warning, const Ref<EditorExportPlatform> &, String); GDVIRTUAL0RC(String, _get_name) + GDVIRTUAL1RC(bool, _supports_platform, const Ref<EditorExportPlatform> &); + + GDVIRTUAL2RC(PackedStringArray, _get_android_dependencies, const Ref<EditorExportPlatform> &, bool); + GDVIRTUAL2RC(PackedStringArray, _get_android_dependencies_maven_repos, const Ref<EditorExportPlatform> &, bool); + GDVIRTUAL2RC(PackedStringArray, _get_android_libraries, const Ref<EditorExportPlatform> &, bool); + GDVIRTUAL2RC(String, _get_android_manifest_activity_element_contents, const Ref<EditorExportPlatform> &, bool); + GDVIRTUAL2RC(String, _get_android_manifest_application_element_contents, const Ref<EditorExportPlatform> &, bool); + GDVIRTUAL2RC(String, _get_android_manifest_element_contents, const Ref<EditorExportPlatform> &, bool); + virtual bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features); // Return true if this plugin does property export customization virtual Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path); // If nothing is returned, it means do not touch (nothing changed). If something is returned (either the same or a different resource) it means changes are made. @@ -142,10 +155,20 @@ protected: virtual PackedStringArray _get_export_features(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const; virtual void _get_export_options(const Ref<EditorExportPlatform> &p_export_platform, List<EditorExportPlatform::ExportOption> *r_options) const; virtual bool _should_update_export_options(const Ref<EditorExportPlatform> &p_export_platform) const; - - virtual String _get_name() const; + virtual String _get_export_option_warning(const Ref<EditorExportPlatform> &p_export_platform, const String &p_option_name) const; public: + virtual String get_name() const; + + virtual bool supports_platform(const Ref<EditorExportPlatform> &p_export_platform) const; + + virtual PackedStringArray get_android_dependencies(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const; + virtual PackedStringArray get_android_dependencies_maven_repos(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const; + virtual PackedStringArray get_android_libraries(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const; + virtual String get_android_manifest_activity_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const; + virtual String get_android_manifest_application_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const; + virtual String get_android_manifest_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const; + Vector<String> get_ios_frameworks() const; Vector<String> get_ios_embedded_frameworks() const; Vector<String> get_ios_project_static_libs() const; diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp index a7dc44e3a8..dfc0c23afc 100644 --- a/editor/export/editor_export_preset.cpp +++ b/editor/export/editor_export_preset.cpp @@ -57,7 +57,26 @@ void EditorExportPreset::_bind_methods() { } String EditorExportPreset::_get_property_warning(const StringName &p_name) const { - return platform->get_export_option_warning(this, p_name); + String warning = platform->get_export_option_warning(this, p_name); + if (!warning.is_empty()) { + warning += "\n"; + } + + // Get property warning from editor export plugins. + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + if (!export_plugins[i]->supports_platform(platform)) { + continue; + } + + export_plugins.write[i]->set_export_preset(Ref<EditorExportPreset>(this)); + String plugin_warning = export_plugins[i]->_get_export_option_warning(platform, p_name); + if (!plugin_warning.is_empty()) { + warning += plugin_warning + "\n"; + } + } + + return warning; } void EditorExportPreset::_get_property_list(List<PropertyInfo> *p_list) const { diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp index e551b0531a..42e4b6f6f6 100644 --- a/editor/export/export_template_manager.cpp +++ b/editor/export/export_template_manager.cpp @@ -669,11 +669,8 @@ Error ExportTemplateManager::install_android_template_from_file(const String &p_ f->store_line(VERSION_FULL_CONFIG); } - // Create the android plugins directory. - Error err = da->make_dir_recursive("android/plugins"); - ERR_FAIL_COND_V(err != OK, err); - - err = da->make_dir_recursive("android/build"); + // Create the android build directory. + Error err = da->make_dir_recursive("android/build"); ERR_FAIL_COND_V(err != OK, err); { // Add an empty .gdignore file to avoid scan. diff --git a/editor/gui/editor_toaster.cpp b/editor/gui/editor_toaster.cpp index 866a6db2a6..f928a0fd30 100644 --- a/editor/gui/editor_toaster.cpp +++ b/editor/gui/editor_toaster.cpp @@ -35,6 +35,7 @@ #include "scene/gui/button.h" #include "scene/gui/label.h" #include "scene/gui/panel_container.h" +#include "scene/resources/style_box_flat.h" EditorToaster *EditorToaster::singleton = nullptr; diff --git a/editor/gui/editor_toaster.h b/editor/gui/editor_toaster.h index 4837756b4e..3e39d9d519 100644 --- a/editor/gui/editor_toaster.h +++ b/editor/gui/editor_toaster.h @@ -37,6 +37,7 @@ class Button; class PanelContainer; +class StyleBoxFlat; class EditorToaster : public HBoxContainer { GDCLASS(EditorToaster, HBoxContainer); diff --git a/editor/import/audio_stream_import_settings.cpp b/editor/import/audio_stream_import_settings.cpp index abf56c7ef8..1f7fbd8c59 100644 --- a/editor/import/audio_stream_import_settings.cpp +++ b/editor/import/audio_stream_import_settings.cpp @@ -395,7 +395,7 @@ void AudioStreamImportSettings::_seek_to(real_t p_x) { void AudioStreamImportSettings::edit(const String &p_path, const String &p_importer, const Ref<AudioStream> &p_stream) { if (!stream.is_null()) { - stream->disconnect("changed", callable_mp(this, &AudioStreamImportSettings::_audio_changed)); + stream->disconnect_changed(callable_mp(this, &AudioStreamImportSettings::_audio_changed)); } importer = p_importer; @@ -408,7 +408,7 @@ void AudioStreamImportSettings::edit(const String &p_path, const String &p_impor _duration_label->set_text(text); if (!stream.is_null()) { - stream->connect("changed", callable_mp(this, &AudioStreamImportSettings::_audio_changed)); + stream->connect_changed(callable_mp(this, &AudioStreamImportSettings::_audio_changed)); _preview->queue_redraw(); _indicator->queue_redraw(); color_rect->queue_redraw(); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index ea210872f7..1abf9d57ce 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -48,6 +48,7 @@ #include "scene/gui/separator.h" #include "scene/gui/view_panner.h" #include "scene/main/window.h" +#include "scene/resources/style_box_flat.h" void AnimationNodeBlendTreeEditor::add_custom_type(const String &p_name, const Ref<Script> &p_script) { for (int i = 0; i < add_options.size(); i++) { diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index 49f073f245..54296411f7 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -50,6 +50,7 @@ #include "scene/gui/tree.h" #include "scene/main/viewport.h" #include "scene/main/window.h" +#include "scene/resources/style_box_flat.h" #include "scene/scene_string_names.h" bool AnimationNodeStateMachineEditor::can_edit(const Ref<AnimationNode> &p_node) { diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp index e01849ff26..89579150c2 100644 --- a/editor/plugins/audio_stream_editor_plugin.cpp +++ b/editor/plugins/audio_stream_editor_plugin.cpp @@ -30,7 +30,6 @@ #include "audio_stream_editor_plugin.h" -#include "core/core_string_names.h" #include "editor/audio_stream_preview.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" @@ -195,7 +194,7 @@ void AudioStreamEditor::_seek_to(real_t p_x) { void AudioStreamEditor::set_stream(const Ref<AudioStream> &p_stream) { if (stream.is_valid()) { - stream->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &AudioStreamEditor::_stream_changed)); + stream->disconnect_changed(callable_mp(this, &AudioStreamEditor::_stream_changed)); } stream = p_stream; @@ -203,7 +202,7 @@ void AudioStreamEditor::set_stream(const Ref<AudioStream> &p_stream) { hide(); return; } - stream->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &AudioStreamEditor::_stream_changed)); + stream->connect_changed(callable_mp(this, &AudioStreamEditor::_stream_changed)); _player->set_stream(stream); _current = 0; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index d6a11f856a..0386a05cc2 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -62,6 +62,7 @@ #include "scene/main/canvas_layer.h" #include "scene/main/window.h" #include "scene/resources/packed_scene.h" +#include "scene/resources/style_box_texture.h" // Min and Max are power of two in order to play nicely with successive increment. // That way, we can naturally reach a 100% zoom from boundaries. diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 4e2a629c7e..2f97cda343 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -39,12 +39,13 @@ class AcceptDialog; class CanvasItemEditorViewport; class ConfirmationDialog; class EditorData; -class EditorZoomWidget; class EditorSelection; +class EditorZoomWidget; class HScrollBar; class HSplitContainer; class MenuButton; class PanelContainer; +class StyleBoxTexture; class ViewPanner; class VScrollBar; class VSplitContainer; diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 75be2b1a60..3f5dae1423 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -31,7 +31,6 @@ #include "curve_editor_plugin.h" #include "canvas_item_editor_plugin.h" -#include "core/core_string_names.h" #include "core/input/input.h" #include "core/math/geometry_2d.h" #include "core/os/keyboard.h" @@ -62,14 +61,14 @@ void CurveEdit::set_curve(Ref<Curve> p_curve) { } if (curve.is_valid()) { - curve->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveEdit::_curve_changed)); + curve->disconnect_changed(callable_mp(this, &CurveEdit::_curve_changed)); curve->disconnect(Curve::SIGNAL_RANGE_CHANGED, callable_mp(this, &CurveEdit::_curve_changed)); } curve = p_curve; if (curve.is_valid()) { - curve->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveEdit::_curve_changed)); + curve->connect_changed(callable_mp(this, &CurveEdit::_curve_changed)); curve->connect(Curve::SIGNAL_RANGE_CHANGED, callable_mp(this, &CurveEdit::_curve_changed)); } diff --git a/editor/plugins/dedicated_server_export_plugin.h b/editor/plugins/dedicated_server_export_plugin.h index cb014ae52d..8991c052b3 100644 --- a/editor/plugins/dedicated_server_export_plugin.h +++ b/editor/plugins/dedicated_server_export_plugin.h @@ -40,7 +40,7 @@ private: EditorExportPreset::FileExportMode _get_export_mode_for_path(const String &p_path); protected: - String _get_name() const override { return "DedicatedServer"; } + String get_name() const override { return "DedicatedServer"; } PackedStringArray _get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const override; uint64_t _get_customization_configuration_hash() const override; diff --git a/editor/plugins/gdextension_export_plugin.h b/editor/plugins/gdextension_export_plugin.h index d1c47ab14e..54e6899796 100644 --- a/editor/plugins/gdextension_export_plugin.h +++ b/editor/plugins/gdextension_export_plugin.h @@ -36,7 +36,7 @@ class GDExtensionExportPlugin : public EditorExportPlugin { protected: virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features); - virtual String _get_name() const { return "GDExtension"; } + virtual String get_name() const { return "GDExtension"; } }; void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) { diff --git a/editor/plugins/gradient_editor.cpp b/editor/plugins/gradient_editor.cpp index e7fc2b1336..59bd0f02fc 100644 --- a/editor/plugins/gradient_editor.cpp +++ b/editor/plugins/gradient_editor.cpp @@ -39,7 +39,7 @@ void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) { gradient = p_gradient; connect("ramp_changed", callable_mp(this, &GradientEditor::_ramp_changed)); - gradient->connect("changed", callable_mp(this, &GradientEditor::_gradient_changed)); + gradient->connect_changed(callable_mp(this, &GradientEditor::_gradient_changed)); set_points(gradient->get_points()); set_interpolation_mode(gradient->get_interpolation_mode()); set_interpolation_color_space(gradient->get_interpolation_color_space()); diff --git a/editor/plugins/gradient_texture_2d_editor_plugin.cpp b/editor/plugins/gradient_texture_2d_editor_plugin.cpp index 034f531f44..c48de7c3dd 100644 --- a/editor/plugins/gradient_texture_2d_editor_plugin.cpp +++ b/editor/plugins/gradient_texture_2d_editor_plugin.cpp @@ -133,7 +133,7 @@ void GradientTexture2DEdit::gui_input(const Ref<InputEvent> &p_event) { void GradientTexture2DEdit::set_texture(Ref<GradientTexture2D> &p_texture) { texture = p_texture; - texture->connect("changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); + texture->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); } void GradientTexture2DEdit::set_snap_enabled(bool p_snap_enabled) { diff --git a/editor/plugins/input_event_editor_plugin.cpp b/editor/plugins/input_event_editor_plugin.cpp index be36447432..9a54a8c1a1 100644 --- a/editor/plugins/input_event_editor_plugin.cpp +++ b/editor/plugins/input_event_editor_plugin.cpp @@ -77,7 +77,7 @@ void InputEventConfigContainer::set_event(const Ref<InputEvent> &p_event) { input_event = p_event; _event_changed(); - input_event->connect("changed", callable_mp(this, &InputEventConfigContainer::_event_changed)); + input_event->connect_changed(callable_mp(this, &InputEventConfigContainer::_event_changed)); } InputEventConfigContainer::InputEventConfigContainer() { diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index d026916668..68d3661d10 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -4929,7 +4929,7 @@ void Node3DEditorViewport::register_shortcut_action(const String &p_path, const Ref<Shortcut> sc = ED_SHORTCUT(p_path, p_name, p_keycode, p_physical); shortcut_changed_callback(sc, p_path); // Connect to the change event on the shortcut so the input binding can be updated. - sc->connect("changed", callable_mp(this, &Node3DEditorViewport::shortcut_changed_callback).bind(sc, p_path)); + sc->connect_changed(callable_mp(this, &Node3DEditorViewport::shortcut_changed_callback).bind(sc, p_path)); } // Update the action in the InputMap to the provided shortcut events. diff --git a/editor/plugins/polygon_3d_editor_plugin.cpp b/editor/plugins/polygon_3d_editor_plugin.cpp index efbb2b0d2b..ceff9cb5f3 100644 --- a/editor/plugins/polygon_3d_editor_plugin.cpp +++ b/editor/plugins/polygon_3d_editor_plugin.cpp @@ -30,7 +30,6 @@ #include "polygon_3d_editor_plugin.h" -#include "core/core_string_names.h" #include "core/input/input.h" #include "core/io/file_access.h" #include "core/math/geometry_2d.h" @@ -497,7 +496,7 @@ void Polygon3DEditor::edit(Node *p_node) { node_resource = node->call("_get_editable_3d_polygon_resource"); if (node_resource.is_valid()) { - node_resource->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Polygon3DEditor::_polygon_draw)); + node_resource->connect_changed(callable_mp(this, &Polygon3DEditor::_polygon_draw)); } //Enable the pencil tool if the polygon is empty if (_get_polygon().is_empty()) { @@ -518,7 +517,7 @@ void Polygon3DEditor::edit(Node *p_node) { } else { node = nullptr; if (node_resource.is_valid()) { - node_resource->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Polygon3DEditor::_polygon_draw)); + node_resource->disconnect_changed(callable_mp(this, &Polygon3DEditor::_polygon_draw)); } node_resource.unref(); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 791937dbf6..875ad1b96d 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -2435,6 +2435,18 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col, return true; } +PackedStringArray ScriptEditor::get_unsaved_scripts() const { + PackedStringArray unsaved_list; + + for (int i = 0; i < tab_container->get_tab_count(); i++) { + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i)); + if (se && se->is_unsaved()) { + unsaved_list.append(se->get_name()); + } + } + return unsaved_list; +} + void ScriptEditor::save_current_script() { ScriptEditorBase *current = _get_current_editor(); if (!current || _test_script_times_on_disk()) { @@ -4207,6 +4219,49 @@ void ScriptEditorPlugin::selected_notify() { _focus_another_editor(); } +String ScriptEditorPlugin::get_unsaved_status(const String &p_for_scene) const { + const PackedStringArray unsaved_scripts = script_editor->get_unsaved_scripts(); + if (unsaved_scripts.is_empty()) { + return String(); + } + + PackedStringArray message; + if (!p_for_scene.is_empty()) { + PackedStringArray unsaved_built_in_scripts; + + const String scene_file = p_for_scene.get_file(); + for (const String &E : unsaved_scripts) { + if (!E.is_resource_file() && E.contains(scene_file)) { + unsaved_built_in_scripts.append(E); + } + } + + if (unsaved_built_in_scripts.is_empty()) { + return String(); + } else { + message.resize(unsaved_built_in_scripts.size() + 1); + message.write[0] = TTR("There are unsaved changes in the following built-in script(s):"); + + int i = 1; + for (const String &E : unsaved_built_in_scripts) { + message.write[i] = E.trim_suffix("(*)"); + i++; + } + return String("\n").join(message); + } + } + + message.resize(unsaved_scripts.size() + 1); + message.write[0] = TTR("Save changes to the following script(s) before quitting?"); + + int i = 1; + for (const String &E : unsaved_scripts) { + message.write[i] = E.trim_suffix("(*)"); + i++; + } + return String("\n").join(message); +} + void ScriptEditorPlugin::save_external_data() { script_editor->save_all_scripts(); } diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index e879920e41..198aaa6c4e 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -512,6 +512,7 @@ public: void get_breakpoints(List<String> *p_breakpoints); + PackedStringArray get_unsaved_scripts() const; void save_current_script(); void save_all_scripts(); @@ -572,6 +573,7 @@ public: virtual void make_visible(bool p_visible) override; virtual void selected_notify() override; + virtual String get_unsaved_status(const String &p_for_scene) const override; virtual void save_external_data() override; virtual void apply_changes() override; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 268828e8f5..247586fbfc 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -287,6 +287,27 @@ void ShaderEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) { p_layout->set_value("ShaderEditor", "selected_shader", selected_shader); } +String ShaderEditorPlugin::get_unsaved_status(const String &p_for_scene) const { + if (!p_for_scene.is_empty()) { + // TODO: handle built-in shaders. + return String(); + } + + // TODO: This should also include visual shaders and shader includes, but save_external_data() doesn't seem to save them... + PackedStringArray unsaved_shaders; + for (uint32_t i = 0; i < edited_shaders.size(); i++) { + if (edited_shaders[i].shader_editor) { + if (edited_shaders[i].shader_editor->is_unsaved()) { + if (unsaved_shaders.is_empty()) { + unsaved_shaders.append(TTR("Save changes to the following shaders(s) before quitting?")); + } + unsaved_shaders.append(edited_shaders[i].shader_editor->get_name()); + } + } + } + return String("\n").join(unsaved_shaders); +} + void ShaderEditorPlugin::save_external_data() { for (EditedShader &edited_shader : edited_shaders) { if (edited_shader.shader_editor) { diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 45b48a2f91..fb7b283266 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -115,6 +115,7 @@ public: virtual void set_window_layout(Ref<ConfigFile> p_layout) override; virtual void get_window_layout(Ref<ConfigFile> p_layout) override; + virtual String get_unsaved_status(const String &p_for_scene) const override; virtual void save_external_data() override; virtual void apply_changes() override; diff --git a/editor/plugins/shader_file_editor_plugin.cpp b/editor/plugins/shader_file_editor_plugin.cpp index f9aa14dd09..b0e532b136 100644 --- a/editor/plugins/shader_file_editor_plugin.cpp +++ b/editor/plugins/shader_file_editor_plugin.cpp @@ -222,7 +222,7 @@ void ShaderFileEditor::_bind_methods() { void ShaderFileEditor::edit(const Ref<RDShaderFile> &p_shader) { if (p_shader.is_null()) { if (shader_file.is_valid()) { - shader_file->disconnect("changed", callable_mp(this, &ShaderFileEditor::_shader_changed)); + shader_file->disconnect_changed(callable_mp(this, &ShaderFileEditor::_shader_changed)); } return; } @@ -234,7 +234,7 @@ void ShaderFileEditor::edit(const Ref<RDShaderFile> &p_shader) { shader_file = p_shader; if (shader_file.is_valid()) { - shader_file->connect("changed", callable_mp(this, &ShaderFileEditor::_shader_changed)); + shader_file->connect_changed(callable_mp(this, &ShaderFileEditor::_shader_changed)); } _update_options(); diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp index c126aec008..1d14f5e60b 100644 --- a/editor/plugins/style_box_editor_plugin.cpp +++ b/editor/plugins/style_box_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "editor/editor_scale.h" #include "scene/gui/button.h" +#include "scene/resources/style_box_texture.h" bool StyleBoxPreview::grid_preview_enabled = true; @@ -42,11 +43,11 @@ void StyleBoxPreview::_grid_preview_toggled(bool p_active) { void StyleBoxPreview::edit(const Ref<StyleBox> &p_stylebox) { if (stylebox.is_valid()) { - stylebox->disconnect("changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); + stylebox->disconnect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); } stylebox = p_stylebox; if (stylebox.is_valid()) { - stylebox->connect("changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); + stylebox->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); } Ref<StyleBoxTexture> sbt = stylebox; grid_preview->set_visible(sbt.is_valid()); diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp index 4838661fa8..ed98c7f85c 100644 --- a/editor/plugins/text_shader_editor.cpp +++ b/editor/plugins/text_shader_editor.cpp @@ -122,7 +122,7 @@ void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader, const Stri return; } if (shader.is_valid()) { - shader->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); + shader->disconnect_changed(callable_mp(this, &ShaderTextEditor::_shader_changed)); } shader = p_shader; shader_inc = Ref<ShaderInclude>(); @@ -130,7 +130,7 @@ void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader, const Stri set_edited_code(p_code); if (shader.is_valid()) { - shader->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); + shader->connect_changed(callable_mp(this, &ShaderTextEditor::_shader_changed)); } } @@ -152,7 +152,7 @@ void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_sha return; } if (shader_inc.is_valid()) { - shader_inc->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); + shader_inc->disconnect_changed(callable_mp(this, &ShaderTextEditor::_shader_changed)); } shader_inc = p_shader_inc; shader = Ref<Shader>(); @@ -160,7 +160,7 @@ void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_sha set_edited_code(p_code); if (shader_inc.is_valid()) { - shader_inc->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); + shader_inc->connect_changed(callable_mp(this, &ShaderTextEditor::_shader_changed)); } } diff --git a/editor/plugins/texture_3d_editor_plugin.cpp b/editor/plugins/texture_3d_editor_plugin.cpp index 9904b888f2..2702e94188 100644 --- a/editor/plugins/texture_3d_editor_plugin.cpp +++ b/editor/plugins/texture_3d_editor_plugin.cpp @@ -115,7 +115,7 @@ void Texture3DEditor::_texture_rect_update_area() { void Texture3DEditor::edit(Ref<Texture3D> p_texture) { if (!texture.is_null()) { - texture->disconnect("changed", callable_mp(this, &Texture3DEditor::_texture_changed)); + texture->disconnect_changed(callable_mp(this, &Texture3DEditor::_texture_changed)); } texture = p_texture; @@ -125,7 +125,7 @@ void Texture3DEditor::edit(Ref<Texture3D> p_texture) { _make_shaders(); } - texture->connect("changed", callable_mp(this, &Texture3DEditor::_texture_changed)); + texture->connect_changed(callable_mp(this, &Texture3DEditor::_texture_changed)); queue_redraw(); texture_rect->set_material(material); setting = true; @@ -176,7 +176,7 @@ Texture3DEditor::Texture3DEditor() { Texture3DEditor::~Texture3DEditor() { if (!texture.is_null()) { - texture->disconnect("changed", callable_mp(this, &Texture3DEditor::_texture_changed)); + texture->disconnect_changed(callable_mp(this, &Texture3DEditor::_texture_changed)); } } diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index ee1a01eb95..87b207ebcd 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -140,7 +140,7 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) { metadata_label = memnew(Label); _update_metadata_label_text(); - p_texture->connect("changed", callable_mp(this, &TexturePreview::_update_metadata_label_text)); + p_texture->connect_changed(callable_mp(this, &TexturePreview::_update_metadata_label_text)); // It's okay that these colors are static since the grid color is static too. metadata_label->add_theme_color_override("font_color", Color::named("white")); diff --git a/editor/plugins/texture_layered_editor_plugin.cpp b/editor/plugins/texture_layered_editor_plugin.cpp index 816d081617..a0188b08e5 100644 --- a/editor/plugins/texture_layered_editor_plugin.cpp +++ b/editor/plugins/texture_layered_editor_plugin.cpp @@ -181,7 +181,7 @@ void TextureLayeredEditor::_texture_rect_update_area() { void TextureLayeredEditor::edit(Ref<TextureLayered> p_texture) { if (!texture.is_null()) { - texture->disconnect("changed", callable_mp(this, &TextureLayeredEditor::_texture_changed)); + texture->disconnect_changed(callable_mp(this, &TextureLayeredEditor::_texture_changed)); } texture = p_texture; @@ -191,7 +191,7 @@ void TextureLayeredEditor::edit(Ref<TextureLayered> p_texture) { _make_shaders(); } - texture->connect("changed", callable_mp(this, &TextureLayeredEditor::_texture_changed)); + texture->connect_changed(callable_mp(this, &TextureLayeredEditor::_texture_changed)); queue_redraw(); texture_rect->set_material(materials[texture->get_layered_type()]); setting = true; diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 8e03b13481..19df31a0b3 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -30,7 +30,6 @@ #include "texture_region_editor_plugin.h" -#include "core/core_string_names.h" #include "core/input/input.h" #include "core/os/keyboard.h" #include "editor/editor_node.h" @@ -433,7 +432,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } else if (obj_styleBox.is_valid()) { undo_redo->add_do_method(obj_styleBox.ptr(), "set_texture_margin", side[edited_margin], obj_styleBox->get_texture_margin(side[edited_margin])); undo_redo->add_undo_method(obj_styleBox.ptr(), "set_texture_margin", side[edited_margin], prev_margin); - obj_styleBox->emit_signal(CoreStringNames::get_singleton()->changed); + obj_styleBox->emit_changed(); } edited_margin = -1; } else { @@ -913,10 +912,10 @@ void TextureRegionEditor::edit(Object *p_obj) { node_ninepatch->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); } if (obj_styleBox.is_valid()) { - obj_styleBox->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); + obj_styleBox->disconnect_changed(callable_mp(this, &TextureRegionEditor::_texture_changed)); } if (atlas_tex.is_valid()) { - atlas_tex->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); + atlas_tex->disconnect_changed(callable_mp(this, &TextureRegionEditor::_texture_changed)); } node_sprite_2d = nullptr; @@ -941,7 +940,7 @@ void TextureRegionEditor::edit(Object *p_obj) { } if (is_resource) { - p_obj->connect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); + Object::cast_to<Resource>(p_obj)->connect_changed(callable_mp(this, &TextureRegionEditor::_texture_changed)); } else { p_obj->connect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); } diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 6b7a94452d..6b7a198246 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -38,7 +38,7 @@ #include "scene/3d/sprite_3d.h" #include "scene/gui/dialogs.h" #include "scene/gui/nine_patch_rect.h" -#include "scene/resources/style_box.h" +#include "scene/resources/style_box_texture.h" class AtlasTexture; class OptionButton; diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 09053db122..a1ddfc4b85 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -3151,7 +3151,7 @@ void ThemeTypeEditor::_stylebox_item_changed(Ref<StyleBox> p_value, String p_ite void ThemeTypeEditor::_change_pinned_stylebox() { if (leading_stylebox.pinned) { if (leading_stylebox.stylebox.is_valid()) { - leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + leading_stylebox.stylebox->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); } Ref<StyleBox> new_stylebox = edited_theme->get_stylebox(leading_stylebox.item_name, edited_type); @@ -3159,10 +3159,10 @@ void ThemeTypeEditor::_change_pinned_stylebox() { leading_stylebox.ref_stylebox = (new_stylebox.is_valid() ? new_stylebox->duplicate() : Ref<Resource>()); if (leading_stylebox.stylebox.is_valid()) { - new_stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + new_stylebox->connect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); } } else if (leading_stylebox.stylebox.is_valid()) { - leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + leading_stylebox.stylebox->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); } } @@ -3187,7 +3187,7 @@ void ThemeTypeEditor::_on_pin_leader_button_pressed(Control *p_editor, String p_ void ThemeTypeEditor::_pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_stylebox) { if (leading_stylebox.stylebox.is_valid()) { - leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + leading_stylebox.stylebox->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); } LeadingStylebox leader; @@ -3198,7 +3198,7 @@ void ThemeTypeEditor::_pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_ leading_stylebox = leader; if (p_stylebox.is_valid()) { - p_stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + p_stylebox->connect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); } _update_type_items(); @@ -3214,7 +3214,7 @@ void ThemeTypeEditor::_on_unpin_leader_button_pressed() { void ThemeTypeEditor::_unpin_leading_stylebox() { if (leading_stylebox.stylebox.is_valid()) { - leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + leading_stylebox.stylebox->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); } LeadingStylebox leader; @@ -3337,12 +3337,12 @@ void ThemeTypeEditor::_bind_methods() { void ThemeTypeEditor::set_edited_theme(const Ref<Theme> &p_theme) { if (edited_theme.is_valid()) { - edited_theme->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced)); + edited_theme->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced)); } edited_theme = p_theme; if (edited_theme.is_valid()) { - edited_theme->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced)); + edited_theme->connect_changed(callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced)); _update_type_list(); } diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 7767831ea3..24e61d7dc9 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -88,11 +88,11 @@ void TileDataEditor::_bind_methods() { void TileDataEditor::set_tile_set(Ref<TileSet> p_tile_set) { if (tile_set.is_valid()) { - tile_set->disconnect("changed", callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update)); + tile_set->disconnect_changed(callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update)); } tile_set = p_tile_set; if (tile_set.is_valid()) { - tile_set->connect("changed", callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update)); + tile_set->connect_changed(callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update)); } _tile_set_changed_plan_update(); } diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 4e8c28e997..3a2d71ddf9 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -2104,7 +2104,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() { void TileSetAtlasSourceEditor::_tile_set_changed() { if (tile_set->get_source_count() == 0) { // No sources, so nothing to do here anymore. - tile_set->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); + tile_set->disconnect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); tile_set = Ref<TileSet>(); return; } @@ -2218,7 +2218,7 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource // Remove listener for old objects. if (tile_set.is_valid()) { - tile_set->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); + tile_set->disconnect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); } // Clear the selection. @@ -2233,7 +2233,7 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource read_only = new_read_only_state; if (tile_set.is_valid()) { - tile_set->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); + tile_set->connect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); } if (read_only && tools_button_group->get_pressed_button() == tool_paint_button) { diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 9821588f63..9725ee72bb 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -723,7 +723,7 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) { // Remove listener. if (tile_set.is_valid()) { - tile_set->disconnect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed)); + tile_set->disconnect_changed(callable_mp(this, &TileSetEditor::_tile_set_changed)); } // Change the edited object. @@ -738,7 +738,7 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) { sources_advanced_menu_button->set_disabled(read_only); source_sort_button->set_disabled(read_only); - tile_set->connect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed)); + tile_set->connect_changed(callable_mp(this, &TileSetEditor::_tile_set_changed)); if (first_edit) { first_edit = false; _set_source_sort(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "tile_source_sort", 0)); diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp index 6908dd7c3b..5ffa7b4bd4 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -384,7 +384,7 @@ void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetS // Remove listener for old objects. if (tile_set_scenes_collection_source) { - tile_set_scenes_collection_source->disconnect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed)); + tile_set_scenes_collection_source->disconnect_changed(callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed)); } // Change the edited object. @@ -404,7 +404,7 @@ void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetS // Add the listener again. if (tile_set_scenes_collection_source) { - tile_set_scenes_collection_source->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed)); + tile_set_scenes_collection_source->connect_changed(callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed)); } // Update everything. diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index ed33115fbf..bd30c3b6b7 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -31,7 +31,6 @@ #include "visual_shader_editor_plugin.h" #include "core/config/project_settings.h" -#include "core/core_string_names.h" #include "core/io/resource_loader.h" #include "core/math/math_defs.h" #include "core/os/keyboard.h" @@ -59,6 +58,7 @@ #include "scene/main/window.h" #include "scene/resources/curve_texture.h" #include "scene/resources/image_texture.h" +#include "scene/resources/style_box_flat.h" #include "scene/resources/visual_shader_nodes.h" #include "scene/resources/visual_shader_particle_nodes.h" #include "servers/display_server.h" @@ -241,7 +241,7 @@ void VisualShaderGraphPlugin::update_curve(int p_node_id) { if (tex->get_texture().is_valid()) { links[p_node_id].curve_editors[0]->set_curve(tex->get_texture()->get_curve()); } - tex->emit_signal(CoreStringNames::get_singleton()->changed); + tex->emit_changed(); } } @@ -255,7 +255,7 @@ void VisualShaderGraphPlugin::update_curve_xyz(int p_node_id) { links[p_node_id].curve_editors[1]->set_curve(tex->get_texture()->get_curve_y()); links[p_node_id].curve_editors[2]->set_curve(tex->get_texture()->get_curve_z()); } - tex->emit_signal(CoreStringNames::get_singleton()->changed); + tex->emit_changed(); } } @@ -562,9 +562,8 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool if (curve.is_valid()) { custom_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); - Callable ce = callable_mp(graph_plugin, &VisualShaderGraphPlugin::update_curve); - if (curve->get_texture().is_valid() && !curve->get_texture()->is_connected("changed", ce)) { - curve->get_texture()->connect("changed", ce.bind(p_id)); + if (curve->get_texture().is_valid()) { + curve->get_texture()->connect_changed(callable_mp(graph_plugin, &VisualShaderGraphPlugin::update_curve).bind(p_id)); } CurveEditor *curve_editor = memnew(CurveEditor); @@ -580,9 +579,8 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool if (curve_xyz.is_valid()) { custom_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); - Callable ce = callable_mp(graph_plugin, &VisualShaderGraphPlugin::update_curve_xyz); - if (curve_xyz->get_texture().is_valid() && !curve_xyz->get_texture()->is_connected("changed", ce)) { - curve_xyz->get_texture()->connect("changed", ce.bind(p_id)); + if (curve_xyz->get_texture().is_valid()) { + curve_xyz->get_texture()->connect_changed(callable_mp(graph_plugin, &VisualShaderGraphPlugin::update_curve_xyz).bind(p_id)); } CurveEditor *curve_editor_x = memnew(CurveEditor); @@ -1161,20 +1159,14 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) { visual_shader = Ref<VisualShader>(p_visual_shader); graph_plugin->register_shader(visual_shader.ptr()); - Callable ce = callable_mp(this, &VisualShaderEditor::_update_preview); - if (!visual_shader->is_connected("changed", ce)) { - visual_shader->connect("changed", ce); - } + visual_shader->connect_changed(callable_mp(this, &VisualShaderEditor::_update_preview)); visual_shader->set_graph_offset(graph->get_scroll_ofs() / EDSCALE); _set_mode(visual_shader->get_mode()); _update_nodes(); } else { if (visual_shader.is_valid()) { - Callable ce = callable_mp(this, &VisualShaderEditor::_update_preview); - if (visual_shader->is_connected("changed", ce)) { - visual_shader->disconnect("changed", ce); - } + visual_shader->disconnect_changed(callable_mp(this, &VisualShaderEditor::_update_preview)); } visual_shader.unref(); } @@ -6449,7 +6441,7 @@ public: properties[i]->update_property(); properties[i]->set_name_split_ratio(0); } - node->connect("changed", callable_mp(this, &VisualShaderNodePluginDefaultEditor::_node_changed)); + node->connect_changed(callable_mp(this, &VisualShaderNodePluginDefaultEditor::_node_changed)); } static void _bind_methods() { @@ -6715,7 +6707,7 @@ void VisualShaderNodePortPreview::_shader_changed() { void VisualShaderNodePortPreview::setup(const Ref<VisualShader> &p_shader, VisualShader::Type p_type, int p_node, int p_port, bool p_is_valid) { shader = p_shader; - shader->connect("changed", callable_mp(this, &VisualShaderNodePortPreview::_shader_changed), CONNECT_DEFERRED); + shader->connect_changed(callable_mp(this, &VisualShaderNodePortPreview::_shader_changed), CONNECT_DEFERRED); type = p_type; port = p_port; node = p_node; diff --git a/main/main.cpp b/main/main.cpp index 989b9bc59d..7e2741648d 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2710,6 +2710,9 @@ bool Main::start() { } uint64_t minimum_time_msec = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/boot_splash/minimum_display_time", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:ms"), 0); + if (Engine::get_singleton()->is_editor_hint()) { + minimum_time_msec = 0; + } #ifdef TOOLS_ENABLED #ifdef MODULE_GDSCRIPT_ENABLED diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index c241f1cabd..4c217dac28 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -953,12 +953,12 @@ void CSGMesh3D::set_mesh(const Ref<Mesh> &p_mesh) { return; } if (mesh.is_valid()) { - mesh->disconnect("changed", callable_mp(this, &CSGMesh3D::_mesh_changed)); + mesh->disconnect_changed(callable_mp(this, &CSGMesh3D::_mesh_changed)); } mesh = p_mesh; if (mesh.is_valid()) { - mesh->connect("changed", callable_mp(this, &CSGMesh3D::_mesh_changed)); + mesh->connect_changed(callable_mp(this, &CSGMesh3D::_mesh_changed)); } _mesh_changed(); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 3d6d133579..7117337827 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -787,11 +787,11 @@ Error GDScript::reload(bool p_keep_state) { err = compiler.compile(&parser, this, p_keep_state); if (err) { + _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT); if (can_run) { if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error()); } - _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT); reloading = false; return ERR_COMPILATION_FAILED; } else { diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 004af80a91..3f571602e8 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -2579,9 +2579,9 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri } } else if (!base->is_valid()) { Error err = OK; - Ref<GDScript> base_root = GDScriptCache::get_full_script(base->path, err, p_script->path); + Ref<GDScript> base_root = GDScriptCache::get_shallow_script(base->path, err, p_script->path); if (err) { - _set_error(vformat(R"(Could not compile base class "%s" from "%s": %s)", base->fully_qualified_name, base->path, error_names[err]), nullptr); + _set_error(vformat(R"(Could not parse base class "%s" from "%s": %s)", base->fully_qualified_name, base->path, error_names[err]), nullptr); return err; } if (base_root.is_valid()) { @@ -2591,7 +2591,12 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri _set_error(vformat(R"(Could not find class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr); return ERR_COMPILATION_FAILED; } - ERR_FAIL_COND_V(!base->is_valid() && !base->reloading, ERR_BUG); + + err = _populate_class_members(base.ptr(), p_class->base_type.class_type, p_keep_state); + if (err) { + _set_error(vformat(R"(Could not populate class members of base class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr); + return err; + } } p_script->base = base; @@ -2968,7 +2973,7 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri GDScriptCache::add_static_script(p_script); } - return GDScriptCache::finish_compiling(main_script->get_path()); + return GDScriptCache::finish_compiling(main_script->path); } String GDScriptCompiler::get_error() const { diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index e23bd50b8b..605e82be6e 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -98,7 +98,7 @@ public: return; } - virtual String _get_name() const override { return "GDScript"; } + virtual String get_name() const override { return "GDScript"; } }; static void _editor_init() { diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 6973bd3cd8..f9c3ca476a 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -138,11 +138,11 @@ Returns the position of a grid cell in the GridMap's local coordinate space. To convert the returned value into global coordinates, use [method Node3D.to_global]. See also [method map_to_local]. </description> </method> - <method name="resource_changed"> + <method name="resource_changed" is_deprecated="true"> <return type="void" /> <param index="0" name="resource" type="Resource" /> <description> - Notifies the [GridMap] about changed resource and recreates octant data. + [i]Obsoleted.[/i] Use [signal Resource.changed] instead. </description> </method> <method name="set_cell_item"> diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index c77fa98be2..f1e2218434 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -258,11 +258,11 @@ RID GridMap::get_navigation_map() const { void GridMap::set_mesh_library(const Ref<MeshLibrary> &p_mesh_library) { if (!mesh_library.is_null()) { - mesh_library->unregister_owner(this); + mesh_library->disconnect_changed(callable_mp(this, &GridMap::_recreate_octant_data)); } mesh_library = p_mesh_library; if (!mesh_library.is_null()) { - mesh_library->register_owner(this); + mesh_library->connect_changed(callable_mp(this, &GridMap::_recreate_octant_data)); } _recreate_octant_data(); @@ -1005,9 +1005,10 @@ void GridMap::clear() { clear_baked_meshes(); } +#ifndef DISABLE_DEPRECATED void GridMap::resource_changed(const Ref<Resource> &p_res) { - _recreate_octant_data(); } +#endif void GridMap::_update_octants_callback() { if (!awaiting_update) { @@ -1079,7 +1080,9 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("map_to_local", "map_position"), &GridMap::map_to_local); ClassDB::bind_method(D_METHOD("_update_octants_callback"), &GridMap::_update_octants_callback); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &GridMap::resource_changed); +#endif ClassDB::bind_method(D_METHOD("set_center_x", "enable"), &GridMap::set_center_x); ClassDB::bind_method(D_METHOD("get_center_x"), &GridMap::get_center_x); @@ -1336,10 +1339,6 @@ void GridMap::_navigation_map_changed(RID p_map) { #endif // DEBUG_ENABLED GridMap::~GridMap() { - if (!mesh_library.is_null()) { - mesh_library->unregister_owner(this); - } - clear(); #ifdef DEBUG_ENABLED NavigationServer3D::get_singleton()->disconnect("map_changed", callable_mp(this, &GridMap::_navigation_map_changed)); diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 18c3f90269..e05979efbc 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -203,7 +203,9 @@ class GridMap : public Node3D { void _queue_octants_dirty(); void _update_octants_callback(); +#ifndef DISABLE_DEPRECATED void resource_changed(const Ref<Resource> &p_res); +#endif void _clear_internal(); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs index 3e6858485d..09a4ab538f 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs @@ -620,7 +620,7 @@ namespace Godot.SourceGenerators bool isPresetHint = false; - if (elementVariantType == VariantType.String) + if (elementVariantType == VariantType.String || elementVariantType == VariantType.StringName) isPresetHint = GetStringArrayEnumHint(elementVariantType, exportAttr, out hintString); if (!isPresetHint) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index 36f5d8e2ab..74425c9835 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using System.ComponentModel; namespace Godot { @@ -623,21 +624,31 @@ namespace Godot /// </summary> /// <param name="target">The position to look at.</param> /// <param name="up">The relative up direction.</param> + /// <param name="useModelFront"> + /// If true, then the model is oriented in reverse, + /// towards the model front axis (+Z, Vector3.ModelFront), + /// which is more useful for orienting 3D models. + /// </param> /// <returns>The resulting basis matrix.</returns> - public static Basis LookingAt(Vector3 target, Vector3 up) + public static Basis LookingAt(Vector3 target, Vector3? up = null, bool useModelFront = false) { + up ??= Vector3.Up; #if DEBUG if (target.IsZeroApprox()) { throw new ArgumentException("The vector can't be zero.", nameof(target)); } - if (up.IsZeroApprox()) + if (up.Value.IsZeroApprox()) { throw new ArgumentException("The vector can't be zero.", nameof(up)); } #endif - Vector3 column2 = -target.Normalized(); - Vector3 column0 = up.Cross(column2); + Vector3 column2 = target.Normalized(); + if (!useModelFront) + { + column2 = -column2; + } + Vector3 column0 = up.Value.Cross(column2); #if DEBUG if (column0.IsZeroApprox()) { @@ -649,6 +660,13 @@ namespace Godot return new Basis(column0, column1, column2); } + /// <inheritdoc cref="LookingAt(Vector3, Nullable{Vector3}, bool)"/> + [EditorBrowsable(EditorBrowsableState.Never)] + public static Basis LookingAt(Vector3 target, Vector3 up) + { + return LookingAt(target, up, false); + } + /// <summary> /// Returns the orthonormalized version of the basis matrix (useful to /// call occasionally to avoid rounding errors for orthogonal matrices). diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs index 1e2aaa299f..ae2c025137 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using System.ComponentModel; namespace Godot { @@ -175,14 +176,26 @@ namespace Godot /// </summary> /// <param name="target">The object to look at.</param> /// <param name="up">The relative up direction.</param> + /// <param name="useModelFront"> + /// If true, then the model is oriented in reverse, + /// towards the model front axis (+Z, Vector3.ModelFront), + /// which is more useful for orienting 3D models. + /// </param> /// <returns>The resulting transform.</returns> - public readonly Transform3D LookingAt(Vector3 target, Vector3 up) + public readonly Transform3D LookingAt(Vector3 target, Vector3? up = null, bool useModelFront = false) { Transform3D t = this; - t.SetLookAt(Origin, target, up); + t.SetLookAt(Origin, target, up ?? Vector3.Up, useModelFront); return t; } + /// <inheritdoc cref="LookingAt(Vector3, Nullable{Vector3}, bool)"/> + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly Transform3D LookingAt(Vector3 target, Vector3 up) + { + return LookingAt(target, up, false); + } + /// <summary> /// Returns the transform with the basis orthogonal (90 degrees), /// and normalized axis vectors (scale of 1 or -1). @@ -247,9 +260,9 @@ namespace Godot return new Transform3D(Basis * tmpBasis, Origin); } - private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up) + private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up, bool useModelFront = false) { - Basis = Basis.LookingAt(target - eye, up); + Basis = Basis.LookingAt(target - eye, up, useModelFront); Origin = eye; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index c773c0fda6..d929b5c6ab 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -660,6 +660,13 @@ namespace Godot private static readonly Vector3 _forward = new Vector3(0, 0, -1); private static readonly Vector3 _back = new Vector3(0, 0, 1); + private static readonly Vector3 _modelLeft = new Vector3(1, 0, 0); + private static readonly Vector3 _modelRight = new Vector3(-1, 0, 0); + private static readonly Vector3 _modelTop = new Vector3(0, 1, 0); + private static readonly Vector3 _modelBottom = new Vector3(0, -1, 0); + private static readonly Vector3 _modelFront = new Vector3(0, 0, 1); + private static readonly Vector3 _modelRear = new Vector3(0, 0, -1); + /// <summary> /// Zero vector, a vector with all components set to <c>0</c>. /// </summary> @@ -712,6 +719,31 @@ namespace Godot public static Vector3 Back { get { return _back; } } /// <summary> + /// Unit vector pointing towards the left side of imported 3D assets. + /// </summary> + public static Vector3 ModelLeft { get { return _modelLeft; } } + /// <summary> + /// Unit vector pointing towards the right side of imported 3D assets. + /// </summary> + public static Vector3 ModelRight { get { return _modelRight; } } + /// <summary> + /// Unit vector pointing towards the top side (up) of imported 3D assets. + /// </summary> + public static Vector3 ModelTop { get { return _modelTop; } } + /// <summary> + /// Unit vector pointing towards the bottom side (down) of imported 3D assets. + /// </summary> + public static Vector3 ModelBottom { get { return _modelBottom; } } + /// <summary> + /// Unit vector pointing towards the front side (facing forward) of imported 3D assets. + /// </summary> + public static Vector3 ModelFront { get { return _modelFront; } } + /// <summary> + /// Unit vector pointing towards the rear side (back) of imported 3D assets. + /// </summary> + public static Vector3 ModelRear { get { return _modelRear; } } + + /// <summary> /// Constructs a new <see cref="Vector3"/> with the given components. /// </summary> /// <param name="x">The vector's X component.</param> diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp index e5207fdae2..9c2d281f72 100644 --- a/modules/multiplayer/multiplayer_synchronizer.cpp +++ b/modules/multiplayer/multiplayer_synchronizer.cpp @@ -157,7 +157,7 @@ Error MultiplayerSynchronizer::get_state(const List<NodePath> &p_properties, Obj bool valid = false; const Object *obj = _get_prop_target(p_obj, prop); ERR_FAIL_COND_V(!obj, FAILED); - r_variant.write[i] = obj->get(prop.get_concatenated_subnames(), &valid); + r_variant.write[i] = obj->get_indexed(prop.get_subnames(), &valid); r_variant_ptrs.write[i] = &r_variant[i]; ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop)); i++; @@ -171,7 +171,7 @@ Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Obj for (const NodePath &prop : p_properties) { Object *obj = _get_prop_target(p_obj, prop); ERR_FAIL_COND_V(!obj, FAILED); - obj->set(prop.get_concatenated_subnames(), p_state[i]); + obj->set_indexed(prop.get_subnames(), p_state[i]); i += 1; } return OK; diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp index 867cf5d8fc..9c0ce3e71e 100644 --- a/modules/navigation/nav_region.cpp +++ b/modules/navigation/nav_region.cpp @@ -115,11 +115,11 @@ void NavRegion::update_polygons() { #ifdef DEBUG_ENABLED if (!Math::is_equal_approx(double(map->get_cell_size()), double(mesh->get_cell_size()))) { - ERR_PRINT_ONCE("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a different `cell_size` than the `cell_size` set on the navigation map."); + ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(map->get_cell_size()), double(mesh->get_cell_size()))); } if (!Math::is_equal_approx(double(map->get_cell_height()), double(mesh->get_cell_height()))) { - ERR_PRINT_ONCE("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a different `cell_height` than the `cell_height` set on the navigation map."); + ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_height` of %s while assigned to a navigation map set to a `cell_height` of %s. The cell height for navigation maps can be changed by using the NavigationServer map_set_cell_height() function. The cell height for default navigation maps can also be changed in the ProjectSettings.", double(map->get_cell_height()), double(mesh->get_cell_height()))); } if (map && Math::rad_to_deg(map->get_up().angle_to(transform.basis.get_column(1))) >= 90.0f) { diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp index a7176e0816..1b0c5cb9e3 100644 --- a/modules/noise/noise_texture_2d.cpp +++ b/modules/noise/noise_texture_2d.cpp @@ -32,8 +32,6 @@ #include "noise.h" -#include "core/core_string_names.h" - NoiseTexture2D::NoiseTexture2D() { noise = Ref<Noise>(); @@ -223,11 +221,11 @@ void NoiseTexture2D::set_noise(Ref<Noise> p_noise) { return; } if (noise.is_valid()) { - noise->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update)); + noise->disconnect_changed(callable_mp(this, &NoiseTexture2D::_queue_update)); } noise = p_noise; if (noise.is_valid()) { - noise->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update)); + noise->connect_changed(callable_mp(this, &NoiseTexture2D::_queue_update)); } _queue_update(); } @@ -347,11 +345,11 @@ void NoiseTexture2D::set_color_ramp(const Ref<Gradient> &p_gradient) { return; } if (color_ramp.is_valid()) { - color_ramp->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update)); + color_ramp->disconnect_changed(callable_mp(this, &NoiseTexture2D::_queue_update)); } color_ramp = p_gradient; if (color_ramp.is_valid()) { - color_ramp->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update)); + color_ramp->connect_changed(callable_mp(this, &NoiseTexture2D::_queue_update)); } _queue_update(); } diff --git a/modules/noise/noise_texture_3d.cpp b/modules/noise/noise_texture_3d.cpp index f6c67b0f2d..ed242e7faa 100644 --- a/modules/noise/noise_texture_3d.cpp +++ b/modules/noise/noise_texture_3d.cpp @@ -32,8 +32,6 @@ #include "noise.h" -#include "core/core_string_names.h" - NoiseTexture3D::NoiseTexture3D() { noise = Ref<Noise>(); @@ -214,11 +212,11 @@ void NoiseTexture3D::set_noise(Ref<Noise> p_noise) { return; } if (noise.is_valid()) { - noise->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture3D::_queue_update)); + noise->disconnect_changed(callable_mp(this, &NoiseTexture3D::_queue_update)); } noise = p_noise; if (noise.is_valid()) { - noise->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture3D::_queue_update)); + noise->connect_changed(callable_mp(this, &NoiseTexture3D::_queue_update)); } _queue_update(); } @@ -297,11 +295,11 @@ void NoiseTexture3D::set_color_ramp(const Ref<Gradient> &p_gradient) { return; } if (color_ramp.is_valid()) { - color_ramp->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture3D::_queue_update)); + color_ramp->disconnect_changed(callable_mp(this, &NoiseTexture3D::_queue_update)); } color_ramp = p_gradient; if (color_ramp.is_valid()) { - color_ramp->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture3D::_queue_update)); + color_ramp->connect_changed(callable_mp(this, &NoiseTexture3D::_queue_update)); } _queue_update(); } diff --git a/modules/noise/tests/test_noise_texture_2d.h b/modules/noise/tests/test_noise_texture_2d.h index e2ec39ef48..938e8fd6ab 100644 --- a/modules/noise/tests/test_noise_texture_2d.h +++ b/modules/noise/tests/test_noise_texture_2d.h @@ -210,7 +210,7 @@ TEST_CASE("[NoiseTexture2D][SceneTree] Generating a basic noise texture with mip noise_texture->set_generate_mipmaps(true); Ref<NoiseTextureTester> tester = memnew(NoiseTextureTester(noise_texture.ptr())); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_mip_and_color_ramp)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_mip_and_color_ramp)); MessageQueue::get_singleton()->flush(); } @@ -227,7 +227,7 @@ TEST_CASE("[NoiseTexture2D][SceneTree] Generating a normal map without mipmaps") noise_texture->set_generate_mipmaps(false); Ref<NoiseTextureTester> tester = memnew(NoiseTextureTester(noise_texture.ptr())); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_normal_map)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_normal_map)); MessageQueue::get_singleton()->flush(); } @@ -245,7 +245,7 @@ TEST_CASE("[NoiseTexture2D][SceneTree] Generating a seamless noise texture") { SUBCASE("Grayscale(L8) 16x16, with seamless blend skirt of 0.05") { noise_texture->set_seamless_blend_skirt(0.05); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_grayscale)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_grayscale)); MessageQueue::get_singleton()->flush(); } @@ -257,7 +257,7 @@ TEST_CASE("[NoiseTexture2D][SceneTree] Generating a seamless noise texture") { gradient->set_points(points); noise_texture->set_color_ramp(gradient); noise_texture->set_seamless_blend_skirt(1.0); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_rgba)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_rgba)); MessageQueue::get_singleton()->flush(); } } diff --git a/modules/noise/tests/test_noise_texture_3d.h b/modules/noise/tests/test_noise_texture_3d.h index a612f2920a..b708eac43b 100644 --- a/modules/noise/tests/test_noise_texture_3d.h +++ b/modules/noise/tests/test_noise_texture_3d.h @@ -194,7 +194,7 @@ TEST_CASE("[NoiseTexture3D][SceneTree] Generating a basic noise texture with mip noise_texture->set_depth(16); Ref<NoiseTexture3DTester> tester = memnew(NoiseTexture3DTester(noise_texture.ptr())); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTexture3DTester::check_mip_and_color_ramp)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTexture3DTester::check_mip_and_color_ramp)); MessageQueue::get_singleton()->flush(); } @@ -213,7 +213,7 @@ TEST_CASE("[NoiseTexture3D][SceneTree] Generating a seamless noise texture") { SUBCASE("Grayscale(L8) 16x16x16, with seamless blend skirt of 0.05") { noise_texture->set_seamless_blend_skirt(0.05); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_grayscale)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_grayscale)); MessageQueue::get_singleton()->flush(); } @@ -225,7 +225,7 @@ TEST_CASE("[NoiseTexture3D][SceneTree] Generating a seamless noise texture") { gradient->set_points(points); noise_texture->set_color_ramp(gradient); noise_texture->set_seamless_blend_skirt(1.0); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_rgba)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_rgba)); MessageQueue::get_singleton()->flush(); } } diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp index 6fffa1ed07..65559afed0 100644 --- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp +++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp @@ -134,6 +134,10 @@ void OpenXRHandTrackingExtension::on_process() { // process our hands const XrTime time = OpenXRAPI::get_singleton()->get_next_frame_time(); // This data will be used for the next frame we render + if (time == 0) { + // we don't have timing info yet, or we're skipping a frame... + return; + } XrResult result; diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index fcd717cfec..b54335b724 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -33,6 +33,7 @@ #include "core/io/file_access.h" #include "core/variant/typed_array.h" +#include "modules/vorbis/resource_importer_ogg_vorbis.h" #include <ogg/ogg.h> int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) { @@ -520,6 +521,9 @@ bool AudioStreamOggVorbis::is_monophonic() const { } void AudioStreamOggVorbis::_bind_methods() { + ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_buffer", "buffer"), &AudioStreamOggVorbis::load_from_buffer); + ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_file", "path"), &AudioStreamOggVorbis::load_from_file); + ClassDB::bind_method(D_METHOD("set_packet_sequence", "packet_sequence"), &AudioStreamOggVorbis::set_packet_sequence); ClassDB::bind_method(D_METHOD("get_packet_sequence"), &AudioStreamOggVorbis::get_packet_sequence); @@ -549,3 +553,11 @@ void AudioStreamOggVorbis::_bind_methods() { AudioStreamOggVorbis::AudioStreamOggVorbis() {} AudioStreamOggVorbis::~AudioStreamOggVorbis() {} + +Ref<AudioStreamOggVorbis> AudioStreamOggVorbis::load_from_buffer(const Vector<uint8_t> &file_data) { + return ResourceImporterOggVorbis::load_from_buffer(file_data); +} + +Ref<AudioStreamOggVorbis> AudioStreamOggVorbis::load_from_file(const String &p_path) { + return ResourceImporterOggVorbis::load_from_file(p_path); +} diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h index c76df7f84d..41ce942eec 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.h +++ b/modules/vorbis/audio_stream_ogg_vorbis.h @@ -125,6 +125,8 @@ protected: static void _bind_methods(); public: + static Ref<AudioStreamOggVorbis> load_from_file(const String &p_path); + static Ref<AudioStreamOggVorbis> load_from_buffer(const Vector<uint8_t> &file_data); void set_loop(bool p_enable); virtual bool has_loop() const override; diff --git a/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml index 4f920e2e04..7e3af6688a 100644 --- a/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml +++ b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml @@ -1,11 +1,29 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="AudioStreamOggVorbis" inherits="AudioStream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> + A class representing an Ogg Vorbis audio stream. </brief_description> <description> + The AudioStreamOggVorbis class is a specialized [AudioStream] for handling Ogg Vorbis file formats. It offers functionality for loading and playing back Ogg Vorbis files, as well as managing looping and other playback properties. This class is part of the audio stream system, which also supports WAV files through the [AudioStreamWAV] class. </description> <tutorials> </tutorials> + <methods> + <method name="load_from_buffer" qualifiers="static"> + <return type="AudioStreamOggVorbis" /> + <param index="0" name="buffer" type="PackedByteArray" /> + <description> + Creates a new AudioStreamOggVorbis instance from the given buffer. The buffer must contain Ogg Vorbis data. + </description> + </method> + <method name="load_from_file" qualifiers="static"> + <return type="AudioStreamOggVorbis" /> + <param index="0" name="path" type="String" /> + <description> + Creates a new AudioStreamOggVorbis instance from the given file path. The file must be in Ogg Vorbis format. + </description> + </method> + </methods> <members> <member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4"> </member> diff --git a/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml index ccedb9c98e..10c87b899f 100644 --- a/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml +++ b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml @@ -6,6 +6,22 @@ </description> <tutorials> </tutorials> + <methods> + <method name="load_from_buffer" qualifiers="static"> + <return type="AudioStreamOggVorbis" /> + <param index="0" name="buffer" type="PackedByteArray" /> + <description> + This method loads audio data from a PackedByteArray buffer into an AudioStreamOggVorbis object. + </description> + </method> + <method name="load_from_file" qualifiers="static"> + <return type="AudioStreamOggVorbis" /> + <param index="0" name="path" type="String" /> + <description> + This method loads audio data from a file into an AudioStreamOggVorbis object. The file path is provided as a string. + </description> + </method> + </methods> <members> <member name="bar_beats" type="int" setter="" getter="" default="4"> </member> diff --git a/modules/vorbis/register_types.cpp b/modules/vorbis/register_types.cpp index 028b7a3086..26af912999 100644 --- a/modules/vorbis/register_types.cpp +++ b/modules/vorbis/register_types.cpp @@ -31,7 +31,10 @@ #include "register_types.h" #include "audio_stream_ogg_vorbis.h" + +#ifdef TOOLS_ENABLED #include "resource_importer_ogg_vorbis.h" +#endif void initialize_vorbis_module(ModuleInitializationLevel p_level) { if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp index 8392750798..b42cd20589 100644 --- a/modules/vorbis/resource_importer_ogg_vorbis.cpp +++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp @@ -81,18 +81,50 @@ void ResourceImporterOggVorbis::get_import_options(const String &p_path, List<Im r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,or_greater"), 4)); } -Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const String &p_path) { - Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(f.is_null(), Ref<AudioStreamOggVorbis>(), "Cannot open file '" + p_path + "'."); +#ifdef TOOLS_ENABLED + +bool ResourceImporterOggVorbis::has_advanced_options() const { + return true; +} + +void ResourceImporterOggVorbis::show_advanced_options(const String &p_path) { + Ref<AudioStreamOggVorbis> ogg_stream = load_from_file(p_path); + if (ogg_stream.is_valid()) { + AudioStreamImportSettings::get_singleton()->edit(p_path, "oggvorbisstr", ogg_stream); + } +} +#endif + +Error ResourceImporterOggVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { + bool loop = p_options["loop"]; + float loop_offset = p_options["loop_offset"]; + double bpm = p_options["bpm"]; + int beat_count = p_options["beat_count"]; + int bar_beats = p_options["bar_beats"]; - uint64_t len = f->get_length(); + Ref<AudioStreamOggVorbis> ogg_vorbis_stream = load_from_file(p_source_file); + if (ogg_vorbis_stream.is_null()) { + return ERR_CANT_OPEN; + } - Vector<uint8_t> file_data; - file_data.resize(len); - uint8_t *w = file_data.ptrw(); + ogg_vorbis_stream->set_loop(loop); + ogg_vorbis_stream->set_loop_offset(loop_offset); + ogg_vorbis_stream->set_bpm(bpm); + ogg_vorbis_stream->set_beat_count(beat_count); + ogg_vorbis_stream->set_bar_beats(bar_beats); + + return ResourceSaver::save(ogg_vorbis_stream, p_save_path + ".oggvorbisstr"); +} - f->get_buffer(w, len); +ResourceImporterOggVorbis::ResourceImporterOggVorbis() { +} + +void ResourceImporterOggVorbis::_bind_methods() { + ClassDB::bind_static_method("ResourceImporterOggVorbis", D_METHOD("load_from_buffer", "buffer"), &ResourceImporterOggVorbis::load_from_buffer); + ClassDB::bind_static_method("ResourceImporterOggVorbis", D_METHOD("load_from_file", "path"), &ResourceImporterOggVorbis::load_from_file); +} +Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_buffer(const Vector<uint8_t> &file_data) { Ref<AudioStreamOggVorbis> ogg_vorbis_stream; ogg_vorbis_stream.instantiate(); @@ -114,7 +146,7 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const Str err = ogg_sync_check(&sync_state); ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err)); while (ogg_sync_pageout(&sync_state, &page) != 1) { - if (cursor >= len) { + if (cursor >= size_t(file_data.size())) { done = true; break; } @@ -123,8 +155,8 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const Str char *sync_buf = ogg_sync_buffer(&sync_state, OGG_SYNC_BUFFER_SIZE); err = ogg_sync_check(&sync_state); ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err)); - ERR_FAIL_COND_V(cursor > len, Ref<AudioStreamOggVorbis>()); - size_t copy_size = len - cursor; + ERR_FAIL_COND_V(cursor > size_t(file_data.size()), Ref<AudioStreamOggVorbis>()); + size_t copy_size = file_data.size() - cursor; if (copy_size > OGG_SYNC_BUFFER_SIZE) { copy_size = OGG_SYNC_BUFFER_SIZE; } @@ -201,40 +233,8 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const Str return ogg_vorbis_stream; } -#ifdef TOOLS_ENABLED - -bool ResourceImporterOggVorbis::has_advanced_options() const { - return true; -} - -void ResourceImporterOggVorbis::show_advanced_options(const String &p_path) { - Ref<AudioStreamOggVorbis> ogg_stream = import_ogg_vorbis(p_path); - if (ogg_stream.is_valid()) { - AudioStreamImportSettings::get_singleton()->edit(p_path, "oggvorbisstr", ogg_stream); - } -} -#endif - -Error ResourceImporterOggVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - bool loop = p_options["loop"]; - float loop_offset = p_options["loop_offset"]; - double bpm = p_options["bpm"]; - int beat_count = p_options["beat_count"]; - int bar_beats = p_options["bar_beats"]; - - Ref<AudioStreamOggVorbis> ogg_vorbis_stream = import_ogg_vorbis(p_source_file); - if (ogg_vorbis_stream.is_null()) { - return ERR_CANT_OPEN; - } - - ogg_vorbis_stream->set_loop(loop); - ogg_vorbis_stream->set_loop_offset(loop_offset); - ogg_vorbis_stream->set_bpm(bpm); - ogg_vorbis_stream->set_beat_count(beat_count); - ogg_vorbis_stream->set_bar_beats(bar_beats); - - return ResourceSaver::save(ogg_vorbis_stream, p_save_path + ".oggvorbisstr"); -} - -ResourceImporterOggVorbis::ResourceImporterOggVorbis() { +Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_file(const String &p_path) { + Vector<uint8_t> file_data = FileAccess::get_file_as_bytes(p_path); + ERR_FAIL_COND_V_MSG(file_data.is_empty(), Ref<AudioStreamOggVorbis>(), "Cannot open file '" + p_path + "'."); + return load_from_buffer(file_data); } diff --git a/modules/vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h index 4874419834..59ae3378a0 100644 --- a/modules/vorbis/resource_importer_ogg_vorbis.h +++ b/modules/vorbis/resource_importer_ogg_vorbis.h @@ -42,16 +42,17 @@ class ResourceImporterOggVorbis : public ResourceImporter { OGG_SYNC_BUFFER_SIZE = 8192, }; -private: - // virtual int get_samples_in_packet(Vector<uint8_t> p_packet) = 0; - - static Ref<AudioStreamOggVorbis> import_ogg_vorbis(const String &p_path); +protected: + static void _bind_methods(); public: #ifdef TOOLS_ENABLED virtual bool has_advanced_options() const override; virtual void show_advanced_options(const String &p_path) override; #endif + + static Ref<AudioStreamOggVorbis> load_from_file(const String &p_path); + static Ref<AudioStreamOggVorbis> load_from_buffer(const Vector<uint8_t> &file_data); virtual void get_recognized_extensions(List<String> *p_extensions) const override; virtual String get_save_extension() const override; virtual String get_resource_type() const override; diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml index 57c1c99962..d61d63d242 100644 --- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml +++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml @@ -584,12 +584,6 @@ <member name="version/name" type="String" setter="" getter=""> Application version visible to the user. </member> - <member name="xr_features/hand_tracking" type="int" setter="" getter=""> - </member> - <member name="xr_features/hand_tracking_frequency" type="int" setter="" getter=""> - </member> - <member name="xr_features/passthrough" type="int" setter="" getter=""> - </member> <member name="xr_features/xr_mode" type="int" setter="" getter=""> </member> </members> diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 9d13432dc3..cdffee9318 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -261,30 +261,32 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) { EditorExportPlatformAndroid *ea = static_cast<EditorExportPlatformAndroid *>(ud); while (!ea->quit_request.is_set()) { - // Check for plugins updates +#ifndef DISABLE_DEPRECATED + // Check for android plugins updates { // Nothing to do if we already know the plugins have changed. - if (!ea->plugins_changed.is_set()) { + if (!ea->android_plugins_changed.is_set()) { Vector<PluginConfigAndroid> loaded_plugins = get_plugins(); - MutexLock lock(ea->plugins_lock); + MutexLock lock(ea->android_plugins_lock); - if (ea->plugins.size() != loaded_plugins.size()) { - ea->plugins_changed.set(); + if (ea->android_plugins.size() != loaded_plugins.size()) { + ea->android_plugins_changed.set(); } else { - for (int i = 0; i < ea->plugins.size(); i++) { - if (ea->plugins[i].name != loaded_plugins[i].name) { - ea->plugins_changed.set(); + for (int i = 0; i < ea->android_plugins.size(); i++) { + if (ea->android_plugins[i].name != loaded_plugins[i].name) { + ea->android_plugins_changed.set(); break; } } } - if (ea->plugins_changed.is_set()) { - ea->plugins = loaded_plugins; + if (ea->android_plugins_changed.is_set()) { + ea->android_plugins = loaded_plugins; } } } +#endif // DISABLE_DEPRECATED // Check for devices updates String adb = get_adb_path(); @@ -628,6 +630,7 @@ Vector<EditorExportPlatformAndroid::ABI> EditorExportPlatformAndroid::get_abis() return abis; } +#ifndef DISABLE_DEPRECATED /// List the gdap files in the directory specified by the p_path parameter. Vector<String> EditorExportPlatformAndroid::list_gdap_files(const String &p_path) { Vector<String> dir_files; @@ -694,6 +697,7 @@ Vector<PluginConfigAndroid> EditorExportPlatformAndroid::get_enabled_plugins(con return enabled_plugins; } +#endif // DISABLE_DEPRECATED Error EditorExportPlatformAndroid::store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method) { zip_fileinfo zipfi = get_zip_fileinfo(); @@ -828,16 +832,6 @@ void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> r_permissions.push_back("android.permission.INTERNET"); } } - - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - if (xr_mode_index == XR_MODE_OPENXR) { - int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required - if (hand_tracking_index > XR_HAND_TRACKING_NONE) { - if (r_permissions.find("com.oculus.permission.HAND_TRACKING") == -1) { - r_permissions.push_back("com.oculus.permission.HAND_TRACKING"); - } - } - } } void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) { @@ -861,8 +855,23 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres } } - manifest_text += _get_xr_features_tag(p_preset, _uses_vulkan()); - manifest_text += _get_application_tag(p_preset, _has_read_write_storage_permission(perms)); + if (_uses_vulkan()) { + manifest_text += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.level\" android:required=\"false\" android:version=\"1\" />\n"; + manifest_text += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.version\" android:required=\"true\" android:version=\"0x400003\" />\n"; + } + + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + if (export_plugins[i]->supports_platform(Ref<EditorExportPlatform>(this))) { + const String contents = export_plugins[i]->get_android_manifest_element_contents(Ref<EditorExportPlatform>(this), p_debug); + if (!contents.is_empty()) { + manifest_text += contents; + manifest_text += "\n"; + } + } + } + + manifest_text += _get_application_tag(Ref<EditorExportPlatform>(this), p_preset, _has_read_write_storage_permission(perms), p_debug); manifest_text += "</manifest>\n"; String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")); @@ -1721,7 +1730,7 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport } } else if (p_name == "gradle_build/use_gradle_build") { bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build"); - String enabled_plugins_names = PluginConfigAndroid::get_plugins_names(get_enabled_plugins(Ref<EditorExportPreset>(p_preset))); + String enabled_plugins_names = _get_plugins_names(Ref<EditorExportPreset>(p_preset)); if (!enabled_plugins_names.is_empty() && !gradle_build_enabled) { return TTR("\"Use Gradle Build\" must be enabled to use the plugins."); } @@ -1731,22 +1740,6 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport if (xr_mode_index == XR_MODE_OPENXR && !gradle_build_enabled) { return TTR("OpenXR requires \"Use Gradle Build\" to be enabled"); } - } else if (p_name == "xr_features/hand_tracking") { - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - int hand_tracking = p_preset->get("xr_features/hand_tracking"); - if (xr_mode_index != XR_MODE_OPENXR) { - if (hand_tracking > XR_HAND_TRACKING_NONE) { - return TTR("\"Hand Tracking\" is only valid when \"XR Mode\" is \"OpenXR\"."); - } - } - } else if (p_name == "xr_features/passthrough") { - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - int passthrough_mode = p_preset->get("xr_features/passthrough"); - if (xr_mode_index != XR_MODE_OPENXR) { - if (passthrough_mode > XR_PASSTHROUGH_NONE) { - return TTR("\"Passthrough\" is only valid when \"XR Mode\" is \"OpenXR\"."); - } - } } else if (p_name == "gradle_build/export_format") { bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build"); if (int(p_preset->get("gradle_build/export_format")) == EXPORT_FORMAT_AAB && !gradle_build_enabled) { @@ -1808,12 +1801,14 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", VULKAN_MIN_SDK_VERSION)), "", false, true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/target_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_TARGET_SDK_VERSION)), "", false, true)); +#ifndef DISABLE_DEPRECATED Vector<PluginConfigAndroid> plugins_configs = get_plugins(); for (int i = 0; i < plugins_configs.size(); i++) { print_verbose("Found Android plugin " + plugins_configs[i].name); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("plugins"), plugins_configs[i].name)), false)); } - plugins_changed.clear(); + android_plugins_changed.clear(); +#endif // DISABLE_DEPRECATED // Android supports multiple architectures in an app bundle, so // we expose each option as a checkbox in the export dialog. @@ -1852,9 +1847,6 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,OpenXR"), XR_MODE_REGULAR, false, true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), XR_HAND_TRACKING_NONE, false, true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking_frequency", PROPERTY_HINT_ENUM, "Low,High"), XR_HAND_TRACKING_FREQUENCY_LOW)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/passthrough", PROPERTY_HINT_ENUM, "None,Optional,Required"), XR_PASSTHROUGH_NONE, false, true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true)); @@ -1892,12 +1884,14 @@ Ref<Texture2D> EditorExportPlatformAndroid::get_logo() const { } bool EditorExportPlatformAndroid::should_update_export_options() { - bool export_options_changed = plugins_changed.is_set(); - if (export_options_changed) { +#ifndef DISABLE_DEPRECATED + if (android_plugins_changed.is_set()) { // don't clear unless we're reporting true, to avoid race - plugins_changed.clear(); + android_plugins_changed.clear(); + return true; } - return export_options_changed; +#endif // DISABLE_DEPRECATED + return false; } bool EditorExportPlatformAndroid::poll_export() { @@ -2697,6 +2691,64 @@ String EditorExportPlatformAndroid::join_abis(const Vector<EditorExportPlatformA return ret; } +String EditorExportPlatformAndroid::_get_plugins_names(const Ref<EditorExportPreset> &p_preset) const { + Vector<String> names; + +#ifndef DISABLE_DEPRECATED + PluginConfigAndroid::get_plugins_names(get_enabled_plugins(p_preset), names); +#endif // DISABLE_DEPRECATED + + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + if (export_plugins[i]->supports_platform(Ref<EditorExportPlatform>(this))) { + names.push_back(export_plugins[i]->get_name()); + } + } + + String plugins_names = String("|").join(names); + return plugins_names; +} + +String EditorExportPlatformAndroid::_resolve_export_plugin_android_library_path(const String &p_android_library_path) const { + String absolute_path; + if (!p_android_library_path.is_empty()) { + if (p_android_library_path.is_absolute_path()) { + absolute_path = ProjectSettings::get_singleton()->globalize_path(p_android_library_path); + } else { + const String export_plugin_absolute_path = String("res://addons/").path_join(p_android_library_path); + absolute_path = ProjectSettings::get_singleton()->globalize_path(export_plugin_absolute_path); + } + } + return absolute_path; +} + +bool EditorExportPlatformAndroid::_is_clean_build_required(const Ref<EditorExportPreset> &p_preset) { + bool first_build = last_gradle_build_time == 0; + bool have_plugins_changed = false; + + String plugin_names = _get_plugins_names(p_preset); + + if (!first_build) { + have_plugins_changed = plugin_names != last_plugin_names; +#ifndef DISABLE_DEPRECATED + if (!have_plugins_changed) { + Vector<PluginConfigAndroid> enabled_plugins = get_enabled_plugins(p_preset); + for (int i = 0; i < enabled_plugins.size(); i++) { + if (enabled_plugins.get(i).last_updated > last_gradle_build_time) { + have_plugins_changed = true; + break; + } + } + } +#endif // DISABLE_DEPRECATED + } + + last_gradle_build_time = OS::get_singleton()->get_unix_time(); + last_plugin_names = plugin_names; + + return have_plugins_changed || first_build; +} + Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { int export_format = int(p_preset->get("gradle_build/export_format")); bool should_sign = p_preset->get("package/signed"); @@ -2854,11 +2906,40 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP String sign_flag = should_sign ? "true" : "false"; String zipalign_flag = "true"; + Vector<String> android_libraries; + Vector<String> android_dependencies; + Vector<String> android_dependencies_maven_repos; + +#ifndef DISABLE_DEPRECATED Vector<PluginConfigAndroid> enabled_plugins = get_enabled_plugins(p_preset); - String local_plugins_binaries = PluginConfigAndroid::get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_LOCAL, enabled_plugins); - String remote_plugins_binaries = PluginConfigAndroid::get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_REMOTE, enabled_plugins); - String custom_maven_repos = PluginConfigAndroid::get_plugins_custom_maven_repos(enabled_plugins); - bool clean_build_required = is_clean_build_required(enabled_plugins); + PluginConfigAndroid::get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_LOCAL, enabled_plugins, android_libraries); + PluginConfigAndroid::get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_REMOTE, enabled_plugins, android_dependencies); + PluginConfigAndroid::get_plugins_custom_maven_repos(enabled_plugins, android_dependencies_maven_repos); +#endif // DISABLE_DEPRECATED + + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + if (export_plugins[i]->supports_platform(Ref<EditorExportPlatform>(this))) { + PackedStringArray export_plugin_android_libraries = export_plugins[i]->get_android_libraries(Ref<EditorExportPlatform>(this), p_debug); + for (int k = 0; k < export_plugin_android_libraries.size(); k++) { + const String resolved_android_library_path = _resolve_export_plugin_android_library_path(export_plugin_android_libraries[k]); + if (!resolved_android_library_path.is_empty()) { + android_libraries.push_back(resolved_android_library_path); + } + } + + PackedStringArray export_plugin_android_dependencies = export_plugins[i]->get_android_dependencies(Ref<EditorExportPlatform>(this), p_debug); + android_dependencies.append_array(export_plugin_android_dependencies); + + PackedStringArray export_plugin_android_dependencies_maven_repos = export_plugins[i]->get_android_dependencies_maven_repos(Ref<EditorExportPlatform>(this), p_debug); + android_dependencies_maven_repos.append_array(export_plugin_android_dependencies_maven_repos); + } + } + + bool clean_build_required = _is_clean_build_required(p_preset); + String combined_android_libraries = String("|").join(android_libraries); + String combined_android_dependencies = String("|").join(android_dependencies); + String combined_android_dependencies_maven_repos = String("|").join(android_dependencies_maven_repos); List<String> cmdline; if (clean_build_required) { @@ -2882,9 +2963,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP cmdline.push_back("-Pexport_version_min_sdk=" + min_sdk_version); // argument to specify the min sdk. cmdline.push_back("-Pexport_version_target_sdk=" + target_sdk_version); // argument to specify the target sdk. cmdline.push_back("-Pexport_enabled_abis=" + enabled_abi_string); // argument to specify enabled ABIs. - cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies. - cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies. - cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies. + cmdline.push_back("-Pplugins_local_binaries=" + combined_android_libraries); // argument to specify the list of android libraries provided by plugins. + cmdline.push_back("-Pplugins_remote_binaries=" + combined_android_dependencies); // argument to specify the list of android dependencies provided by plugins. + cmdline.push_back("-Pplugins_maven_repos=" + combined_android_dependencies_maven_repos); // argument to specify the list of maven repos for android dependencies provided by plugins. cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned. cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed. cmdline.push_back("-Pgodot_editor_version=" + String(VERSION_FULL_CONFIG)); @@ -3310,7 +3391,9 @@ EditorExportPlatformAndroid::EditorExportPlatformAndroid() { #endif devices_changed.set(); - plugins_changed.set(); +#ifndef DISABLE_DEPRECATED + android_plugins_changed.set(); +#endif // DISABLE_DEPRECATED #ifndef ANDROID_ENABLED check_for_changes_thread.start(_check_for_changes_poll_thread, this); #endif diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h index 0ac0fbb10b..a2d0417c5d 100644 --- a/platform/android/export/export_plugin.h +++ b/platform/android/export/export_plugin.h @@ -31,7 +31,9 @@ #ifndef ANDROID_EXPORT_PLUGIN_H #define ANDROID_EXPORT_PLUGIN_H +#ifndef DISABLE_DEPRECATED #include "godot_plugin_config.h" +#endif // DISABLE_DEPRECATED #include "core/io/zip_io.h" #include "core/os/os.h" @@ -81,11 +83,14 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { EditorProgress *ep = nullptr; }; - mutable Vector<PluginConfigAndroid> plugins; +#ifndef DISABLE_DEPRECATED + mutable Vector<PluginConfigAndroid> android_plugins; + mutable SafeFlag android_plugins_changed; + Mutex android_plugins_lock; +#endif // DISABLE_DEPRECATED String last_plugin_names; uint64_t last_gradle_build_time = 0; - mutable SafeFlag plugins_changed; - Mutex plugins_lock; + Vector<Device> devices; SafeFlag devices_changed; Mutex device_lock; @@ -128,12 +133,14 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { static Vector<ABI> get_abis(); +#ifndef DISABLE_DEPRECATED /// List the gdap files in the directory specified by the p_path parameter. static Vector<String> list_gdap_files(const String &p_path); static Vector<PluginConfigAndroid> get_plugins(); static Vector<PluginConfigAndroid> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets); +#endif // DISABLE_DEPRECATED static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED); @@ -224,28 +231,11 @@ public: virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; - inline bool is_clean_build_required(Vector<PluginConfigAndroid> enabled_plugins) { - String plugin_names = PluginConfigAndroid::get_plugins_names(enabled_plugins); - bool first_build = last_gradle_build_time == 0; - bool have_plugins_changed = false; - - if (!first_build) { - have_plugins_changed = plugin_names != last_plugin_names; - if (!have_plugins_changed) { - for (int i = 0; i < enabled_plugins.size(); i++) { - if (enabled_plugins.get(i).last_updated > last_gradle_build_time) { - have_plugins_changed = true; - break; - } - } - } - } + String _get_plugins_names(const Ref<EditorExportPreset> &p_preset) const; - last_gradle_build_time = OS::get_singleton()->get_unix_time(); - last_plugin_names = plugin_names; + String _resolve_export_plugin_android_library_path(const String &p_android_library_path) const; - return have_plugins_changed || first_build; - } + bool _is_clean_build_required(const Ref<EditorExportPreset> &p_preset); String get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path); diff --git a/platform/android/export/godot_plugin_config.cpp b/platform/android/export/godot_plugin_config.cpp index b64cca3254..cdec5f55b7 100644 --- a/platform/android/export/godot_plugin_config.cpp +++ b/platform/android/export/godot_plugin_config.cpp @@ -30,6 +30,8 @@ #include "godot_plugin_config.h" +#ifndef DISABLE_DEPRECATED + /* * Set of prebuilt plugins. * Currently unused, this is just for future reference: @@ -145,10 +147,8 @@ PluginConfigAndroid PluginConfigAndroid::load_plugin_config(Ref<ConfigFile> conf return plugin_config; } -String PluginConfigAndroid::get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs) { - String plugins_binaries; +void PluginConfigAndroid::get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result) { if (!plugins_configs.is_empty()) { - Vector<String> binaries; for (int i = 0; i < plugins_configs.size(); i++) { PluginConfigAndroid config = plugins_configs[i]; if (!config.valid_config) { @@ -156,56 +156,44 @@ String PluginConfigAndroid::get_plugins_binaries(String binary_type, Vector<Plug } if (config.binary_type == binary_type) { - binaries.push_back(config.binary); + r_result.push_back(config.binary); } if (binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL) { - binaries.append_array(config.local_dependencies); + r_result.append_array(config.local_dependencies); } if (binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE) { - binaries.append_array(config.remote_dependencies); + r_result.append_array(config.remote_dependencies); } } - - plugins_binaries = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(binaries); } - - return plugins_binaries; } -String PluginConfigAndroid::get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs) { - String custom_maven_repos; +void PluginConfigAndroid::get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result) { if (!plugins_configs.is_empty()) { - Vector<String> repos_urls; for (int i = 0; i < plugins_configs.size(); i++) { PluginConfigAndroid config = plugins_configs[i]; if (!config.valid_config) { continue; } - repos_urls.append_array(config.custom_maven_repos); + r_result.append_array(config.custom_maven_repos); } - - custom_maven_repos = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(repos_urls); } - return custom_maven_repos; } -String PluginConfigAndroid::get_plugins_names(Vector<PluginConfigAndroid> plugins_configs) { - String plugins_names; +void PluginConfigAndroid::get_plugins_names(Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result) { if (!plugins_configs.is_empty()) { - Vector<String> names; for (int i = 0; i < plugins_configs.size(); i++) { PluginConfigAndroid config = plugins_configs[i]; if (!config.valid_config) { continue; } - names.push_back(config.name); + r_result.push_back(config.name); } - plugins_names = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(names); } - - return plugins_names; } + +#endif // DISABLE_DEPRECATED diff --git a/platform/android/export/godot_plugin_config.h b/platform/android/export/godot_plugin_config.h index bef00979a9..8c56d00187 100644 --- a/platform/android/export/godot_plugin_config.h +++ b/platform/android/export/godot_plugin_config.h @@ -31,6 +31,8 @@ #ifndef ANDROID_GODOT_PLUGIN_CONFIG_H #define ANDROID_GODOT_PLUGIN_CONFIG_H +#ifndef DISABLE_DEPRECATED + #include "core/config/project_settings.h" #include "core/error/error_list.h" #include "core/io/config_file.h" @@ -67,8 +69,6 @@ struct PluginConfigAndroid { inline static const char *BINARY_TYPE_LOCAL = "local"; inline static const char *BINARY_TYPE_REMOTE = "remote"; - inline static const char *PLUGIN_VALUE_SEPARATOR = "|"; - // Set to true when the config file is properly loaded. bool valid_config = false; // Unix timestamp of last change to this plugin. @@ -96,11 +96,13 @@ struct PluginConfigAndroid { static PluginConfigAndroid load_plugin_config(Ref<ConfigFile> config_file, const String &path); - static String get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs); + static void get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result); - static String get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs); + static void get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result); - static String get_plugins_names(Vector<PluginConfigAndroid> plugins_configs); + static void get_plugins_names(Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result); }; +#endif // DISABLE_DEPRECATED + #endif // ANDROID_GODOT_PLUGIN_CONFIG_H diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index 8912877faa..d0d0c34bb4 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -254,34 +254,7 @@ String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) { return manifest_screen_sizes; } -String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_vulkan) { - String manifest_xr_features; - int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode")); - bool uses_xr = xr_mode_index == XR_MODE_OPENXR; - if (uses_xr) { - int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required - if (hand_tracking_index == XR_HAND_TRACKING_OPTIONAL) { - manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n"; - } else if (hand_tracking_index == XR_HAND_TRACKING_REQUIRED) { - manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"true\" />\n"; - } - - int passthrough_mode = p_preset->get("xr_features/passthrough"); - if (passthrough_mode == XR_PASSTHROUGH_OPTIONAL) { - manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"com.oculus.feature.PASSTHROUGH\" android:required=\"false\" />\n"; - } else if (passthrough_mode == XR_PASSTHROUGH_REQUIRED) { - manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"com.oculus.feature.PASSTHROUGH\" android:required=\"true\" />\n"; - } - } - - if (p_uses_vulkan) { - manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.level\" android:required=\"false\" android:version=\"1\" />\n"; - manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.version\" android:required=\"true\" android:version=\"0x400003\" />\n"; - } - return manifest_xr_features; -} - -String _get_activity_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_xr) { +String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug) { String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")))); String manifest_activity_text = vformat( " <activity android:name=\"com.godot.game.GodotApp\" " @@ -298,20 +271,6 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_xr " <action android:name=\"android.intent.action.MAIN\" />\n" " <category android:name=\"android.intent.category.LAUNCHER\" />\n"; - if (p_uses_xr) { - manifest_activity_text += "\n" - " <!-- Enable access to OpenXR on Oculus mobile devices, no-op on other Android\n" - " platforms. -->\n" - " <category android:name=\"com.oculus.intent.category.VR\" />\n" - "\n" - " <!-- OpenXR category tag to indicate the activity starts in an immersive OpenXR mode. \n" - " See https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#android-runtime-category. -->\n" - " <category android:name=\"org.khronos.openxr.intent.category.IMMERSIVE_HMD\" />\n" - "\n" - " <!-- Enable VR access on HTC Vive Focus devices. -->\n" - " <category android:name=\"com.htc.intent.category.VRAPP\" />\n"; - } - bool uses_leanback_category = p_preset->get("package/show_in_android_tv"); if (uses_leanback_category) { manifest_activity_text += " <category android:name=\"android.intent.category.LEANBACK_LAUNCHER\" />\n"; @@ -323,18 +282,27 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_xr manifest_activity_text += " <category android:name=\"android.intent.category.DEFAULT\" />\n"; } - manifest_activity_text += " </intent-filter>\n" - " </activity>\n"; + manifest_activity_text += " </intent-filter>\n"; + + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + if (export_plugins[i]->supports_platform(p_export_platform)) { + const String contents = export_plugins[i]->get_android_manifest_activity_element_contents(p_export_platform, p_debug); + if (!contents.is_empty()) { + manifest_activity_text += contents; + manifest_activity_text += "\n"; + } + } + } + + manifest_activity_text += " </activity>\n"; return manifest_activity_text; } -String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission) { +String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission, bool p_debug) { int app_category_index = (int)(p_preset->get("package/app_category")); bool is_game = app_category_index == APP_CATEGORY_GAME; - int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode")); - bool uses_xr = xr_mode_index == XR_MODE_OPENXR; - String manifest_application_text = vformat( " <application android:label=\"@string/godot_project_name_string\"\n" " android:allowBackup=\"%s\"\n" @@ -351,18 +319,18 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_ bool_to_string(p_preset->get("package/retain_data_on_uninstall")), bool_to_string(p_has_read_write_storage_permission)); - if (uses_xr) { - bool hand_tracking_enabled = (int)(p_preset->get("xr_features/hand_tracking")) > XR_HAND_TRACKING_NONE; - if (hand_tracking_enabled) { - int hand_tracking_frequency_index = p_preset->get("xr_features/hand_tracking_frequency"); - String hand_tracking_frequency = hand_tracking_frequency_index == XR_HAND_TRACKING_FREQUENCY_LOW ? "LOW" : "HIGH"; - manifest_application_text += vformat( - " <meta-data tools:node=\"replace\" android:name=\"com.oculus.handtracking.frequency\" android:value=\"%s\" />\n", - hand_tracking_frequency); - manifest_application_text += " <meta-data tools:node=\"replace\" android:name=\"com.oculus.handtracking.version\" android:value=\"V2.0\" />\n"; + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + if (export_plugins[i]->supports_platform(p_export_platform)) { + const String contents = export_plugins[i]->get_android_manifest_application_element_contents(p_export_platform, p_debug); + if (!contents.is_empty()) { + manifest_application_text += contents; + manifest_application_text += "\n"; + } } } - manifest_application_text += _get_activity_tag(p_preset, uses_xr); + + manifest_application_text += _get_activity_tag(p_export_platform, p_preset, p_debug); manifest_application_text += " </application>\n"; return manifest_application_text; } diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h index 8a885a0d12..2498394add 100644 --- a/platform/android/export/gradle_export_util.h +++ b/platform/android/export/gradle_export_util.h @@ -61,20 +61,6 @@ static const int APP_CATEGORY_VIDEO = 8; static const int XR_MODE_REGULAR = 0; static const int XR_MODE_OPENXR = 1; -// Supported XR hand tracking modes. -static const int XR_HAND_TRACKING_NONE = 0; -static const int XR_HAND_TRACKING_OPTIONAL = 1; -static const int XR_HAND_TRACKING_REQUIRED = 2; - -// Supported XR hand tracking frequencies. -static const int XR_HAND_TRACKING_FREQUENCY_LOW = 0; -static const int XR_HAND_TRACKING_FREQUENCY_HIGH = 1; - -// Supported XR passthrough modes. -static const int XR_PASSTHROUGH_NONE = 0; -static const int XR_PASSTHROUGH_OPTIONAL = 1; -static const int XR_PASSTHROUGH_REQUIRED = 2; - struct CustomExportData { String assets_directory; bool debug; @@ -116,10 +102,8 @@ String _get_gles_tag(); String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset); -String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_vulkan); - -String _get_activity_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_xr); +String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug); -String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission); +String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission, bool p_debug); #endif // ANDROID_GRADLE_EXPORT_UTIL_H diff --git a/platform/android/java/app/src/com/godot/game/GodotApp.java b/platform/android/java/app/src/com/godot/game/GodotApp.java index 1d2cc05715..9142d767b4 100644 --- a/platform/android/java/app/src/com/godot/game/GodotApp.java +++ b/platform/android/java/app/src/com/godot/game/GodotApp.java @@ -30,7 +30,7 @@ package com.godot.game; -import org.godotengine.godot.FullScreenGodotApp; +import org.godotengine.godot.GodotActivity; import android.os.Bundle; @@ -38,7 +38,7 @@ import android.os.Bundle; * Template activity for Godot Android builds. * Feel free to extend and modify this class for your custom logic. */ -public class GodotApp extends FullScreenGodotApp { +public class GodotApp extends GodotActivity { @Override public void onCreate(Bundle savedInstanceState) { setTheme(R.style.GodotAppMainTheme); diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt index 64d3d4eca1..7cedfa6888 100644 --- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt +++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt @@ -39,7 +39,7 @@ import android.os.* import android.util.Log import android.widget.Toast import androidx.window.layout.WindowMetricsCalculator -import org.godotengine.godot.FullScreenGodotApp +import org.godotengine.godot.GodotActivity import org.godotengine.godot.GodotLib import org.godotengine.godot.utils.PermissionsUtil import org.godotengine.godot.utils.ProcessPhoenix @@ -55,7 +55,7 @@ import kotlin.math.min * * It also plays the role of the primary editor window. */ -open class GodotEditor : FullScreenGodotApp() { +open class GodotEditor : GodotActivity() { companion object { private val TAG = GodotEditor::class.java.simpleName @@ -115,7 +115,7 @@ open class GodotEditor : FullScreenGodotApp() { runOnUiThread { // Enable long press, panning and scaling gestures - godotFragment?.renderView?.inputHandler?.apply { + godotFragment?.godot?.renderView?.inputHandler?.apply { enableLongPress(longPressEnabled) enablePanningAndScalingGestures(panScaleEnabled) } @@ -318,7 +318,7 @@ open class GodotEditor : FullScreenGodotApp() { override fun onRequestPermissionsResult( requestCode: Int, - permissions: Array<String?>, + permissions: Array<String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) diff --git a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java index 3e975449d8..91d272735e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java +++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java @@ -30,156 +30,10 @@ package org.godotengine.godot; -import org.godotengine.godot.utils.ProcessPhoenix; - -import android.content.Intent; -import android.os.Bundle; -import android.util.Log; - -import androidx.annotation.CallSuper; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; - /** - * Base activity for Android apps intending to use Godot as the primary and only screen. + * Base abstract activity for Android apps intending to use Godot as the primary screen. * - * It's also a reference implementation for how to setup and use the {@link Godot} fragment - * within an Android app. + * @deprecated Use {@link GodotActivity} */ -public abstract class FullScreenGodotApp extends FragmentActivity implements GodotHost { - private static final String TAG = FullScreenGodotApp.class.getSimpleName(); - - protected static final String EXTRA_FORCE_QUIT = "force_quit_requested"; - protected static final String EXTRA_NEW_LAUNCH = "new_launch_requested"; - - @Nullable - private Godot godotFragment; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.godot_app_layout); - - handleStartIntent(getIntent(), true); - - Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.godot_fragment_container); - if (currentFragment instanceof Godot) { - Log.v(TAG, "Reusing existing Godot fragment instance."); - godotFragment = (Godot)currentFragment; - } else { - Log.v(TAG, "Creating new Godot fragment instance."); - godotFragment = initGodotInstance(); - getSupportFragmentManager().beginTransaction().replace(R.id.godot_fragment_container, godotFragment).setPrimaryNavigationFragment(godotFragment).commitNowAllowingStateLoss(); - } - } - - @Override - public void onDestroy() { - Log.v(TAG, "Destroying Godot app..."); - super.onDestroy(); - terminateGodotInstance(godotFragment); - } - - @Override - public final void onGodotForceQuit(Godot instance) { - runOnUiThread(() -> { - terminateGodotInstance(instance); - }); - } - - private void terminateGodotInstance(Godot instance) { - if (instance == godotFragment) { - Log.v(TAG, "Force quitting Godot instance"); - ProcessPhoenix.forceQuit(FullScreenGodotApp.this); - } - } - - @Override - public final void onGodotRestartRequested(Godot instance) { - runOnUiThread(() -> { - if (instance == godotFragment) { - // It's very hard to properly de-initialize Godot on Android to restart the game - // from scratch. Therefore, we need to kill the whole app process and relaunch it. - // - // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including - // releasing and reloading native libs or resetting their state somehow and clearing static data). - Log.v(TAG, "Restarting Godot instance..."); - ProcessPhoenix.triggerRebirth(FullScreenGodotApp.this); - } - }); - } - - @Override - public void onNewIntent(Intent intent) { - super.onNewIntent(intent); - setIntent(intent); - - handleStartIntent(intent, false); - - if (godotFragment != null) { - godotFragment.onNewIntent(intent); - } - } - - private void handleStartIntent(Intent intent, boolean newLaunch) { - boolean forceQuitRequested = intent.getBooleanExtra(EXTRA_FORCE_QUIT, false); - if (forceQuitRequested) { - Log.d(TAG, "Force quit requested, terminating.."); - ProcessPhoenix.forceQuit(this); - return; - } - - if (!newLaunch) { - boolean newLaunchRequested = intent.getBooleanExtra(EXTRA_NEW_LAUNCH, false); - if (newLaunchRequested) { - Log.d(TAG, "New launch requested, restarting.."); - - Intent restartIntent = new Intent(intent).putExtra(EXTRA_NEW_LAUNCH, false); - ProcessPhoenix.triggerRebirth(this, restartIntent); - return; - } - } - } - - @CallSuper - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (godotFragment != null) { - godotFragment.onActivityResult(requestCode, resultCode, data); - } - } - - @CallSuper - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (godotFragment != null) { - godotFragment.onRequestPermissionsResult(requestCode, permissions, grantResults); - } - } - - @Override - public void onBackPressed() { - if (godotFragment != null) { - godotFragment.onBackPressed(); - } else { - super.onBackPressed(); - } - } - - /** - * Used to initialize the Godot fragment instance in {@link FullScreenGodotApp#onCreate(Bundle)}. - */ - @NonNull - protected Godot initGodotInstance() { - return new Godot(); - } - - @Nullable - protected final Godot getGodotFragment() { - return godotFragment; - } -} +@Deprecated +public abstract class FullScreenGodotApp extends GodotActivity {} diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java deleted file mode 100644 index 9f2dec7317..0000000000 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ /dev/null @@ -1,1195 +0,0 @@ -/**************************************************************************/ -/* Godot.java */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -package org.godotengine.godot; - -import static android.content.Context.MODE_PRIVATE; -import static android.content.Context.WINDOW_SERVICE; - -import org.godotengine.godot.input.GodotEditText; -import org.godotengine.godot.io.directory.DirectoryAccessHandler; -import org.godotengine.godot.io.file.FileAccessHandler; -import org.godotengine.godot.plugin.GodotPlugin; -import org.godotengine.godot.plugin.GodotPluginRegistry; -import org.godotengine.godot.tts.GodotTTS; -import org.godotengine.godot.utils.BenchmarkUtils; -import org.godotengine.godot.utils.GodotNetUtils; -import org.godotengine.godot.utils.PermissionsUtil; -import org.godotengine.godot.xr.XRMode; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.ActivityManager; -import android.app.AlertDialog; -import android.app.PendingIntent; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.content.pm.ConfigurationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.Rect; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.os.Messenger; -import android.os.VibrationEffect; -import android.os.Vibrator; -import android.util.Log; -import android.view.Display; -import android.view.LayoutInflater; -import android.view.Surface; -import android.view.SurfaceView; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.view.ViewTreeObserver; -import android.view.Window; -import android.view.WindowInsets; -import android.view.WindowInsetsAnimation; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.FrameLayout; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.CallSuper; -import androidx.annotation.Keep; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.fragment.app.Fragment; - -import com.google.android.vending.expansion.downloader.DownloadProgressInfo; -import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; -import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller; -import com.google.android.vending.expansion.downloader.Helpers; -import com.google.android.vending.expansion.downloader.IDownloaderClient; -import com.google.android.vending.expansion.downloader.IDownloaderService; -import com.google.android.vending.expansion.downloader.IStub; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.security.MessageDigest; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; - -public class Godot extends Fragment implements SensorEventListener, IDownloaderClient { - private static final String TAG = Godot.class.getSimpleName(); - - private IStub mDownloaderClientStub; - private TextView mStatusText; - private TextView mProgressFraction; - private TextView mProgressPercent; - private TextView mAverageSpeed; - private TextView mTimeRemaining; - private ProgressBar mPB; - private ClipboardManager mClipboard; - - private View mDashboard; - private View mCellMessage; - - private Button mPauseButton; - private Button mWiFiSettingsButton; - - private XRMode xrMode = XRMode.REGULAR; - private boolean use_immersive = false; - private boolean use_debug_opengl = false; - private boolean mStatePaused; - private boolean activityResumed; - private int mState; - - private GodotHost godotHost; - private GodotPluginRegistry pluginRegistry; - - static private Intent mCurrentIntent; - - public void onNewIntent(Intent intent) { - mCurrentIntent = intent; - } - - static public Intent getCurrentIntent() { - return mCurrentIntent; - } - - private void setState(int newState) { - if (mState != newState) { - mState = newState; - mStatusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState)); - } - } - - private void setButtonPausedState(boolean paused) { - mStatePaused = paused; - int stringResourceID = paused ? R.string.text_button_resume : R.string.text_button_pause; - mPauseButton.setText(stringResourceID); - } - - private String[] command_line; - private boolean use_apk_expansion; - - private ViewGroup containerLayout; - public GodotRenderView mRenderView; - private boolean godot_initialized = false; - - private SensorManager mSensorManager; - private Sensor mAccelerometer; - private Sensor mGravity; - private Sensor mMagnetometer; - private Sensor mGyroscope; - - public GodotIO io; - public GodotNetUtils netUtils; - public GodotTTS tts; - private DirectoryAccessHandler directoryAccessHandler; - private FileAccessHandler fileAccessHandler; - - public interface ResultCallback { - void callback(int requestCode, int resultCode, Intent data); - } - public ResultCallback result_callback; - - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (getParentFragment() instanceof GodotHost) { - godotHost = (GodotHost)getParentFragment(); - } else if (getActivity() instanceof GodotHost) { - godotHost = (GodotHost)getActivity(); - } - } - - @Override - public void onDetach() { - super.onDetach(); - godotHost = null; - } - - @CallSuper - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (result_callback != null) { - result_callback.callback(requestCode, resultCode, data); - result_callback = null; - } - - for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { - plugin.onMainActivityResult(requestCode, resultCode, data); - } - } - - @CallSuper - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { - plugin.onMainRequestPermissionsResult(requestCode, permissions, grantResults); - } - - for (int i = 0; i < permissions.length; i++) { - GodotLib.requestPermissionResult(permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED); - } - } - - /** - * Invoked on the render thread when the Godot setup is complete. - */ - @CallSuper - protected void onGodotSetupCompleted() { - for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { - plugin.onGodotSetupCompleted(); - } - - if (godotHost != null) { - godotHost.onGodotSetupCompleted(); - } - } - - /** - * Invoked on the render thread when the Godot main loop has started. - */ - @CallSuper - protected void onGodotMainLoopStarted() { - for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { - plugin.onGodotMainLoopStarted(); - } - - if (godotHost != null) { - godotHost.onGodotMainLoopStarted(); - } - } - - /** - * Used by the native code (java_godot_lib_jni.cpp) to complete initialization of the GLSurfaceView view and renderer. - */ - @Keep - private boolean onVideoInit() { - final Activity activity = requireActivity(); - containerLayout = new FrameLayout(activity); - containerLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - - // GodotEditText layout - GodotEditText editText = new GodotEditText(activity); - editText.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, - (int)getResources().getDimension(R.dimen.text_edit_height))); - // ...add to FrameLayout - containerLayout.addView(editText); - - tts = new GodotTTS(activity); - - if (!GodotLib.setup(command_line, tts)) { - Log.e(TAG, "Unable to setup the Godot engine! Aborting..."); - alert(R.string.error_engine_setup_message, R.string.text_error_title, this::forceQuit); - return false; - } - - if (usesVulkan()) { - if (!meetsVulkanRequirements(activity.getPackageManager())) { - alert(R.string.error_missing_vulkan_requirements_message, R.string.text_error_title, this::forceQuit); - return false; - } - mRenderView = new GodotVulkanRenderView(activity, this); - } else { - // Fallback to openGl - mRenderView = new GodotGLRenderView(activity, this, xrMode, use_debug_opengl); - } - - View view = mRenderView.getView(); - containerLayout.addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - editText.setView(mRenderView); - io.setEdit(editText); - - // Listeners for keyboard height. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - // Report the height of virtual keyboard as it changes during the animation. - final View decorView = activity.getWindow().getDecorView(); - decorView.setWindowInsetsAnimationCallback(new WindowInsetsAnimation.Callback(WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP) { - int startBottom, endBottom; - @Override - public void onPrepare(@NonNull WindowInsetsAnimation animation) { - startBottom = decorView.getRootWindowInsets().getInsets(WindowInsets.Type.ime()).bottom; - } - - @NonNull - @Override - public WindowInsetsAnimation.Bounds onStart(@NonNull WindowInsetsAnimation animation, @NonNull WindowInsetsAnimation.Bounds bounds) { - endBottom = decorView.getRootWindowInsets().getInsets(WindowInsets.Type.ime()).bottom; - return bounds; - } - - @NonNull - @Override - public WindowInsets onProgress(@NonNull WindowInsets windowInsets, @NonNull List<WindowInsetsAnimation> list) { - // Find the IME animation. - WindowInsetsAnimation imeAnimation = null; - for (WindowInsetsAnimation animation : list) { - if ((animation.getTypeMask() & WindowInsets.Type.ime()) != 0) { - imeAnimation = animation; - break; - } - } - // Update keyboard height based on IME animation. - if (imeAnimation != null) { - float interpolatedFraction = imeAnimation.getInterpolatedFraction(); - // Linear interpolation between start and end values. - float keyboardHeight = startBottom * (1.0f - interpolatedFraction) + endBottom * interpolatedFraction; - GodotLib.setVirtualKeyboardHeight((int)keyboardHeight); - } - return windowInsets; - } - - @Override - public void onEnd(@NonNull WindowInsetsAnimation animation) { - } - }); - } else { - // Infer the virtual keyboard height using visible area. - view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - // Don't allocate a new Rect every time the callback is called. - final Rect visibleSize = new Rect(); - - @Override - public void onGlobalLayout() { - final SurfaceView view = mRenderView.getView(); - view.getWindowVisibleDisplayFrame(visibleSize); - final int keyboardHeight = view.getHeight() - visibleSize.bottom; - GodotLib.setVirtualKeyboardHeight(keyboardHeight); - } - }); - } - - mRenderView.queueOnRenderThread(() -> { - for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { - plugin.onRegisterPluginWithGodotNative(); - } - setKeepScreenOn(Boolean.parseBoolean(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on"))); - }); - - // Include the returned non-null views in the Godot view hierarchy. - for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { - View pluginView = plugin.onMainCreate(activity); - if (pluginView != null) { - if (plugin.shouldBeOnTop()) { - containerLayout.addView(pluginView); - } else { - containerLayout.addView(pluginView, 0); - } - } - } - return true; - } - - /** - * Returns true if `Vulkan` is used for rendering. - */ - private boolean usesVulkan() { - final String renderer = GodotLib.getGlobal("rendering/renderer/rendering_method"); - final String renderingDevice = GodotLib.getGlobal("rendering/rendering_device/driver"); - return ("forward_plus".equals(renderer) || "mobile".equals(renderer)) && "vulkan".equals(renderingDevice); - } - - /** - * Returns true if the device meets the base requirements for Vulkan support, false otherwise. - */ - private boolean meetsVulkanRequirements(@Nullable PackageManager packageManager) { - if (packageManager == null) { - return false; - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - if (!packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL, 1)) { - // Optional requirements.. log as warning if missing - Log.w(TAG, "The vulkan hardware level does not meet the minimum requirement: 1"); - } - - // Check for api version 1.0 - return packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x400003); - } - - return false; - } - - public void setKeepScreenOn(final boolean p_enabled) { - runOnUiThread(() -> { - if (p_enabled) { - getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } else { - getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - }); - } - - /** - * Used by the native code (java_godot_wrapper.h) to vibrate the device. - * @param durationMs - */ - @SuppressLint("MissingPermission") - @Keep - private void vibrate(int durationMs) { - if (durationMs > 0 && requestPermission("VIBRATE")) { - Vibrator v = (Vibrator)getContext().getSystemService(Context.VIBRATOR_SERVICE); - if (v != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - v.vibrate(VibrationEffect.createOneShot(durationMs, VibrationEffect.DEFAULT_AMPLITUDE)); - } else { - // deprecated in API 26 - v.vibrate(durationMs); - } - } - } - } - - public void restart() { - if (godotHost != null) { - godotHost.onGodotRestartRequested(this); - } - } - - public void alert(final String message, final String title) { - alert(message, title, null); - } - - private void alert(@StringRes int messageResId, @StringRes int titleResId, @Nullable Runnable okCallback) { - Resources res = getResources(); - alert(res.getString(messageResId), res.getString(titleResId), okCallback); - } - - private void alert(final String message, final String title, @Nullable Runnable okCallback) { - final Activity activity = getActivity(); - runOnUiThread(() -> { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setMessage(message).setTitle(title); - builder.setPositiveButton( - "OK", - (dialog, id) -> { - if (okCallback != null) { - okCallback.run(); - } - dialog.cancel(); - }); - AlertDialog dialog = builder.create(); - dialog.show(); - }); - } - - public int getGLESVersionCode() { - ActivityManager am = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE); - ConfigurationInfo deviceInfo = am.getDeviceConfigurationInfo(); - return deviceInfo.reqGlEsVersion; - } - - @CallSuper - protected String[] getCommandLine() { - String[] original = parseCommandLine(); - String[] updated; - List<String> hostCommandLine = godotHost != null ? godotHost.getCommandLine() : null; - if (hostCommandLine == null || hostCommandLine.isEmpty()) { - updated = original; - } else { - updated = Arrays.copyOf(original, original.length + hostCommandLine.size()); - for (int i = 0; i < hostCommandLine.size(); i++) { - updated[original.length + i] = hostCommandLine.get(i); - } - } - return updated; - } - - private String[] parseCommandLine() { - InputStream is; - try { - is = getActivity().getAssets().open("_cl_"); - byte[] len = new byte[4]; - int r = is.read(len); - if (r < 4) { - return new String[0]; - } - int argc = ((int)(len[3] & 0xFF) << 24) | ((int)(len[2] & 0xFF) << 16) | ((int)(len[1] & 0xFF) << 8) | ((int)(len[0] & 0xFF)); - String[] cmdline = new String[argc]; - - for (int i = 0; i < argc; i++) { - r = is.read(len); - if (r < 4) { - return new String[0]; - } - int strlen = ((int)(len[3] & 0xFF) << 24) | ((int)(len[2] & 0xFF) << 16) | ((int)(len[1] & 0xFF) << 8) | ((int)(len[0] & 0xFF)); - if (strlen > 65535) { - return new String[0]; - } - byte[] arg = new byte[strlen]; - r = is.read(arg); - if (r == strlen) { - cmdline[i] = new String(arg, "UTF-8"); - } - } - return cmdline; - } catch (Exception e) { - // The _cl_ file can be missing with no adverse effect - return new String[0]; - } - } - - /** - * Used by the native code (java_godot_wrapper.h) to check whether the activity is resumed or paused. - */ - @Keep - private boolean isActivityResumed() { - return activityResumed; - } - - /** - * Used by the native code (java_godot_wrapper.h) to access the Android surface. - */ - @Keep - private Surface getSurface() { - return mRenderView.getView().getHolder().getSurface(); - } - - /** - * Used by the native code (java_godot_wrapper.h) to access the input fallback mapping. - * @return The input fallback mapping for the current XR mode. - */ - @Keep - private String getInputFallbackMapping() { - return xrMode.inputFallbackMapping; - } - - String expansion_pack_path; - - private void initializeGodot() { - if (expansion_pack_path != null) { - String[] new_cmdline; - int cll = 0; - if (command_line != null) { - new_cmdline = new String[command_line.length + 2]; - cll = command_line.length; - for (int i = 0; i < command_line.length; i++) { - new_cmdline[i] = command_line[i]; - } - } else { - new_cmdline = new String[2]; - } - - new_cmdline[cll] = "--main-pack"; - new_cmdline[cll + 1] = expansion_pack_path; - command_line = new_cmdline; - } - - final Activity activity = getActivity(); - io = new GodotIO(activity); - netUtils = new GodotNetUtils(activity); - Context context = getContext(); - directoryAccessHandler = new DirectoryAccessHandler(context); - fileAccessHandler = new FileAccessHandler(context); - mSensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE); - mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - mGravity = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY); - mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); - mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); - - godot_initialized = GodotLib.initialize(activity, - this, - activity.getAssets(), - io, - netUtils, - directoryAccessHandler, - fileAccessHandler, - use_apk_expansion); - - result_callback = null; - } - - @Override - public void onServiceConnected(Messenger m) { - IDownloaderService remoteService = DownloaderServiceMarshaller.CreateProxy(m); - remoteService.onClientUpdated(mDownloaderClientStub.getMessenger()); - } - - @Override - public void onCreate(Bundle icicle) { - BenchmarkUtils.beginBenchmarkMeasure("Godot::onCreate"); - super.onCreate(icicle); - - final Activity activity = getActivity(); - Window window = activity.getWindow(); - window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - mClipboard = (ClipboardManager)activity.getSystemService(Context.CLIPBOARD_SERVICE); - pluginRegistry = GodotPluginRegistry.initializePluginRegistry(this); - - // check for apk expansion API - boolean md5mismatch = false; - command_line = getCommandLine(); - String main_pack_md5 = null; - String main_pack_key = null; - - List<String> new_args = new LinkedList<>(); - - for (int i = 0; i < command_line.length; i++) { - boolean has_extra = i < command_line.length - 1; - if (command_line[i].equals(XRMode.REGULAR.cmdLineArg)) { - xrMode = XRMode.REGULAR; - } else if (command_line[i].equals(XRMode.OPENXR.cmdLineArg)) { - xrMode = XRMode.OPENXR; - } else if (command_line[i].equals("--debug_opengl")) { - use_debug_opengl = true; - } else if (command_line[i].equals("--use_immersive")) { - use_immersive = true; - window.getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE | - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar - View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - UiChangeListener(); - } else if (command_line[i].equals("--use_apk_expansion")) { - use_apk_expansion = true; - } else if (has_extra && command_line[i].equals("--apk_expansion_md5")) { - main_pack_md5 = command_line[i + 1]; - i++; - } else if (has_extra && command_line[i].equals("--apk_expansion_key")) { - main_pack_key = command_line[i + 1]; - SharedPreferences prefs = activity.getSharedPreferences("app_data_keys", - MODE_PRIVATE); - Editor editor = prefs.edit(); - editor.putString("store_public_key", main_pack_key); - - editor.apply(); - i++; - } else if (command_line[i].equals("--benchmark")) { - BenchmarkUtils.setUseBenchmark(true); - new_args.add(command_line[i]); - } else if (has_extra && command_line[i].equals("--benchmark-file")) { - BenchmarkUtils.setUseBenchmark(true); - new_args.add(command_line[i]); - - // Retrieve the filepath - BenchmarkUtils.setBenchmarkFile(command_line[i + 1]); - new_args.add(command_line[i + 1]); - - i++; - } else if (command_line[i].trim().length() != 0) { - new_args.add(command_line[i]); - } - } - - if (new_args.isEmpty()) { - command_line = null; - } else { - command_line = new_args.toArray(new String[new_args.size()]); - } - if (use_apk_expansion && main_pack_md5 != null && main_pack_key != null) { - // check that environment is ok! - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - // show popup and die - } - - // Build the full path to the app's expansion files - try { - expansion_pack_path = Helpers.getSaveFilePath(getContext()); - expansion_pack_path += "/main." + activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0).versionCode + "." + activity.getPackageName() + ".obb"; - } catch (Exception e) { - e.printStackTrace(); - } - - File f = new File(expansion_pack_path); - - boolean pack_valid = true; - - if (!f.exists()) { - pack_valid = false; - - } else if (obbIsCorrupted(expansion_pack_path, main_pack_md5)) { - pack_valid = false; - try { - f.delete(); - } catch (Exception e) { - } - } - - if (!pack_valid) { - Intent notifierIntent = new Intent(activity, activity.getClass()); - notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - - PendingIntent pendingIntent; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - pendingIntent = PendingIntent.getActivity(activity, 0, - notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - } else { - pendingIntent = PendingIntent.getActivity(activity, 0, - notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); - } - - int startResult; - try { - startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired( - getContext(), - pendingIntent, - GodotDownloaderService.class); - - if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { - // This is where you do set up to display the download - // progress (next step in onCreateView) - mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this, - GodotDownloaderService.class); - - return; - } - } catch (NameNotFoundException e) { - // TODO Auto-generated catch block - } - } - } - - mCurrentIntent = activity.getIntent(); - - initializeGodot(); - BenchmarkUtils.endBenchmarkMeasure("Godot::onCreate"); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle) { - if (mDownloaderClientStub != null) { - View downloadingExpansionView = - inflater.inflate(R.layout.downloading_expansion, container, false); - mPB = (ProgressBar)downloadingExpansionView.findViewById(R.id.progressBar); - mStatusText = (TextView)downloadingExpansionView.findViewById(R.id.statusText); - mProgressFraction = (TextView)downloadingExpansionView.findViewById(R.id.progressAsFraction); - mProgressPercent = (TextView)downloadingExpansionView.findViewById(R.id.progressAsPercentage); - mAverageSpeed = (TextView)downloadingExpansionView.findViewById(R.id.progressAverageSpeed); - mTimeRemaining = (TextView)downloadingExpansionView.findViewById(R.id.progressTimeRemaining); - mDashboard = downloadingExpansionView.findViewById(R.id.downloaderDashboard); - mCellMessage = downloadingExpansionView.findViewById(R.id.approveCellular); - mPauseButton = (Button)downloadingExpansionView.findViewById(R.id.pauseButton); - mWiFiSettingsButton = (Button)downloadingExpansionView.findViewById(R.id.wifiSettingsButton); - - return downloadingExpansionView; - } - - return containerLayout; - } - - @Override - public void onDestroy() { - for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { - plugin.onMainDestroy(); - } - - GodotLib.ondestroy(); - - super.onDestroy(); - - forceQuit(); - } - - @Override - public void onPause() { - super.onPause(); - activityResumed = false; - - if (!godot_initialized) { - if (null != mDownloaderClientStub) { - mDownloaderClientStub.disconnect(getActivity()); - } - return; - } - mRenderView.onActivityPaused(); - - mSensorManager.unregisterListener(this); - - for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { - plugin.onMainPause(); - } - } - - public boolean hasClipboard() { - return mClipboard.hasPrimaryClip(); - } - - public String getClipboard() { - ClipData clipData = mClipboard.getPrimaryClip(); - if (clipData == null) - return ""; - CharSequence text = clipData.getItemAt(0).getText(); - if (text == null) - return ""; - return text.toString(); - } - - public void setClipboard(String p_text) { - ClipData clip = ClipData.newPlainText("myLabel", p_text); - mClipboard.setPrimaryClip(clip); - } - - @Override - public void onResume() { - super.onResume(); - activityResumed = true; - if (!godot_initialized) { - if (null != mDownloaderClientStub) { - mDownloaderClientStub.connect(getActivity()); - } - return; - } - - mRenderView.onActivityResumed(); - - mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); - mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME); - mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME); - mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME); - - if (use_immersive) { - Window window = getActivity().getWindow(); - window.getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE | - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar - View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } - - for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { - plugin.onMainResume(); - } - } - - public void UiChangeListener() { - final View decorView = getActivity().getWindow().getDecorView(); - decorView.setOnSystemUiVisibilityChangeListener(visibility -> { - if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { - decorView.setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE | - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_FULLSCREEN | - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } - }); - } - - public float[] getRotatedValues(float values[]) { - if (values == null || values.length != 3) { - return values; - } - - Display display = - ((WindowManager)getActivity().getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); - int displayRotation = display.getRotation(); - - float[] rotatedValues = new float[3]; - switch (displayRotation) { - case Surface.ROTATION_0: - rotatedValues[0] = values[0]; - rotatedValues[1] = values[1]; - rotatedValues[2] = values[2]; - break; - case Surface.ROTATION_90: - rotatedValues[0] = -values[1]; - rotatedValues[1] = values[0]; - rotatedValues[2] = values[2]; - break; - case Surface.ROTATION_180: - rotatedValues[0] = -values[0]; - rotatedValues[1] = -values[1]; - rotatedValues[2] = values[2]; - break; - case Surface.ROTATION_270: - rotatedValues[0] = values[1]; - rotatedValues[1] = -values[0]; - rotatedValues[2] = values[2]; - break; - } - - return rotatedValues; - } - - @Override - public void onSensorChanged(SensorEvent event) { - if (mRenderView == null) { - return; - } - - final int typeOfSensor = event.sensor.getType(); - switch (typeOfSensor) { - case Sensor.TYPE_ACCELEROMETER: { - float[] rotatedValues = getRotatedValues(event.values); - mRenderView.queueOnRenderThread(() -> { - GodotLib.accelerometer(-rotatedValues[0], -rotatedValues[1], -rotatedValues[2]); - }); - break; - } - case Sensor.TYPE_GRAVITY: { - float[] rotatedValues = getRotatedValues(event.values); - mRenderView.queueOnRenderThread(() -> { - GodotLib.gravity(-rotatedValues[0], -rotatedValues[1], -rotatedValues[2]); - }); - break; - } - case Sensor.TYPE_MAGNETIC_FIELD: { - float[] rotatedValues = getRotatedValues(event.values); - mRenderView.queueOnRenderThread(() -> { - GodotLib.magnetometer(-rotatedValues[0], -rotatedValues[1], -rotatedValues[2]); - }); - break; - } - case Sensor.TYPE_GYROSCOPE: { - float[] rotatedValues = getRotatedValues(event.values); - mRenderView.queueOnRenderThread(() -> { - GodotLib.gyroscope(rotatedValues[0], rotatedValues[1], rotatedValues[2]); - }); - break; - } - } - } - - @Override - public final void onAccuracyChanged(Sensor sensor, int accuracy) { - // Do something here if sensor accuracy changes. - } - - public void onBackPressed() { - boolean shouldQuit = true; - - for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { - if (plugin.onMainBackPressed()) { - shouldQuit = false; - } - } - - if (shouldQuit && mRenderView != null) { - mRenderView.queueOnRenderThread(GodotLib::back); - } - } - - /** - * Queue a runnable to be run on the render thread. - * <p> - * This must be called after the render thread has started. - */ - public final void runOnRenderThread(@NonNull Runnable action) { - if (mRenderView != null) { - mRenderView.queueOnRenderThread(action); - } - } - - public final void runOnUiThread(@NonNull Runnable action) { - if (getActivity() != null) { - getActivity().runOnUiThread(action); - } - } - - private void forceQuit() { - // TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each - // native Godot components that is started in Godot#onVideoInit. - forceQuit(0); - } - - @Keep - private boolean forceQuit(int instanceId) { - if (godotHost == null) { - return false; - } - if (instanceId == 0) { - godotHost.onGodotForceQuit(this); - return true; - } else { - return godotHost.onGodotForceQuit(instanceId); - } - } - - private boolean obbIsCorrupted(String f, String main_pack_md5) { - try { - InputStream fis = new FileInputStream(f); - - // Create MD5 Hash - byte[] buffer = new byte[16384]; - - MessageDigest complete = MessageDigest.getInstance("MD5"); - int numRead; - do { - numRead = fis.read(buffer); - if (numRead > 0) { - complete.update(buffer, 0, numRead); - } - } while (numRead != -1); - - fis.close(); - byte[] messageDigest = complete.digest(); - - // Create Hex String - StringBuilder hexString = new StringBuilder(); - for (byte b : messageDigest) { - String s = Integer.toHexString(0xFF & b); - if (s.length() == 1) { - s = "0" + s; - } - hexString.append(s); - } - String md5str = hexString.toString(); - - if (!md5str.equals(main_pack_md5)) { - return true; - } - return false; - } catch (Exception e) { - e.printStackTrace(); - return true; - } - } - - public boolean requestPermission(String p_name) { - return PermissionsUtil.requestPermission(p_name, getActivity()); - } - - public boolean requestPermissions() { - return PermissionsUtil.requestManifestPermissions(getActivity()); - } - - public String[] getGrantedPermissions() { - return PermissionsUtil.getGrantedPermissions(getActivity()); - } - - @Keep - private String getCACertificates() { - return GodotNetUtils.getCACertificates(); - } - - /** - * The download state should trigger changes in the UI --- it may be useful - * to show the state as being indeterminate at times. This sample can be - * considered a guideline. - */ - @Override - public void onDownloadStateChanged(int newState) { - setState(newState); - boolean showDashboard = true; - boolean showCellMessage = false; - boolean paused; - boolean indeterminate; - switch (newState) { - case IDownloaderClient.STATE_IDLE: - // STATE_IDLE means the service is listening, so it's - // safe to start making remote service calls. - paused = false; - indeterminate = true; - break; - case IDownloaderClient.STATE_CONNECTING: - case IDownloaderClient.STATE_FETCHING_URL: - showDashboard = true; - paused = false; - indeterminate = true; - break; - case IDownloaderClient.STATE_DOWNLOADING: - paused = false; - showDashboard = true; - indeterminate = false; - break; - - case IDownloaderClient.STATE_FAILED_CANCELED: - case IDownloaderClient.STATE_FAILED: - case IDownloaderClient.STATE_FAILED_FETCHING_URL: - case IDownloaderClient.STATE_FAILED_UNLICENSED: - paused = true; - showDashboard = false; - indeterminate = false; - break; - case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION: - case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION: - showDashboard = false; - paused = true; - indeterminate = false; - showCellMessage = true; - break; - - case IDownloaderClient.STATE_PAUSED_BY_REQUEST: - paused = true; - indeterminate = false; - break; - case IDownloaderClient.STATE_PAUSED_ROAMING: - case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE: - paused = true; - indeterminate = false; - break; - case IDownloaderClient.STATE_COMPLETED: - showDashboard = false; - paused = false; - indeterminate = false; - initializeGodot(); - return; - default: - paused = true; - indeterminate = true; - showDashboard = true; - } - int newDashboardVisibility = showDashboard ? View.VISIBLE : View.GONE; - if (mDashboard.getVisibility() != newDashboardVisibility) { - mDashboard.setVisibility(newDashboardVisibility); - } - int cellMessageVisibility = showCellMessage ? View.VISIBLE : View.GONE; - if (mCellMessage.getVisibility() != cellMessageVisibility) { - mCellMessage.setVisibility(cellMessageVisibility); - } - - mPB.setIndeterminate(indeterminate); - setButtonPausedState(paused); - } - - @Override - public void onDownloadProgress(DownloadProgressInfo progress) { - mAverageSpeed.setText(getString(R.string.kilobytes_per_second, - Helpers.getSpeedString(progress.mCurrentSpeed))); - mTimeRemaining.setText(getString(R.string.time_remaining, - Helpers.getTimeRemaining(progress.mTimeRemaining))); - - mPB.setMax((int)(progress.mOverallTotal >> 8)); - mPB.setProgress((int)(progress.mOverallProgress >> 8)); - mProgressPercent.setText(String.format(Locale.ENGLISH, "%d %%", progress.mOverallProgress * 100 / progress.mOverallTotal)); - mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress, - progress.mOverallTotal)); - } - - public void initInputDevices() { - mRenderView.initInputDevices(); - } - - @Keep - public GodotRenderView getRenderView() { // used by native side to get renderView - return mRenderView; - } - - @Keep - public DirectoryAccessHandler getDirectoryAccessHandler() { - return directoryAccessHandler; - } - - @Keep - public FileAccessHandler getFileAccessHandler() { - return fileAccessHandler; - } - - @Keep - private int createNewGodotInstance(String[] args) { - if (godotHost != null) { - return godotHost.onNewGodotInstanceRequested(args); - } - return 0; - } - - @Keep - private void beginBenchmarkMeasure(String label) { - BenchmarkUtils.beginBenchmarkMeasure(label); - } - - @Keep - private void endBenchmarkMeasure(String label) { - BenchmarkUtils.endBenchmarkMeasure(label); - } - - @Keep - private void dumpBenchmark(String benchmarkFile) { - BenchmarkUtils.dumpBenchmark(fileAccessHandler, benchmarkFile); - } -} diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt new file mode 100644 index 0000000000..23de01a191 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -0,0 +1,965 @@ +/**************************************************************************/ +/* Godot.kt */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +package org.godotengine.godot + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.AlertDialog +import android.content.* +import android.content.pm.PackageManager +import android.content.res.Resources +import android.graphics.Rect +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager +import android.os.* +import android.util.Log +import android.view.* +import android.view.ViewTreeObserver.OnGlobalLayoutListener +import android.widget.FrameLayout +import androidx.annotation.Keep +import androidx.annotation.StringRes +import com.google.android.vending.expansion.downloader.* +import org.godotengine.godot.input.GodotEditText +import org.godotengine.godot.io.directory.DirectoryAccessHandler +import org.godotengine.godot.io.file.FileAccessHandler +import org.godotengine.godot.plugin.GodotPluginRegistry +import org.godotengine.godot.tts.GodotTTS +import org.godotengine.godot.utils.GodotNetUtils +import org.godotengine.godot.utils.PermissionsUtil +import org.godotengine.godot.utils.PermissionsUtil.requestPermission +import org.godotengine.godot.utils.beginBenchmarkMeasure +import org.godotengine.godot.utils.benchmarkFile +import org.godotengine.godot.utils.dumpBenchmark +import org.godotengine.godot.utils.endBenchmarkMeasure +import org.godotengine.godot.utils.useBenchmark +import org.godotengine.godot.xr.XRMode +import java.io.File +import java.io.FileInputStream +import java.io.InputStream +import java.nio.charset.StandardCharsets +import java.security.MessageDigest +import java.util.* + +/** + * Core component used to interface with the native layer of the engine. + * + * Can be hosted by [Activity], [Fragment] or [Service] android components, so long as its + * lifecycle methods are properly invoked. + */ +class Godot(private val context: Context) : SensorEventListener { + + private companion object { + private val TAG = Godot::class.java.simpleName + } + + private val pluginRegistry: GodotPluginRegistry by lazy { + GodotPluginRegistry.initializePluginRegistry(this) + } + private val mSensorManager: SensorManager by lazy { + requireActivity().getSystemService(Context.SENSOR_SERVICE) as SensorManager + } + private val mAccelerometer: Sensor by lazy { + mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) + } + private val mGravity: Sensor by lazy { + mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) + } + private val mMagnetometer: Sensor by lazy { + mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) + } + private val mGyroscope: Sensor by lazy { + mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) + } + private val mClipboard: ClipboardManager by lazy { + requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + } + + private val uiChangeListener = View.OnSystemUiVisibilityChangeListener { visibility: Int -> + if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) { + val decorView = requireActivity().window.decorView + decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or + View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + }} + + val tts = GodotTTS(context) + val directoryAccessHandler = DirectoryAccessHandler(context) + val fileAccessHandler = FileAccessHandler(context) + val netUtils = GodotNetUtils(context) + + /** + * Tracks whether [onCreate] was completed successfully. + */ + private var initializationStarted = false + + /** + * Tracks whether [GodotLib.initialize] was completed successfully. + */ + private var nativeLayerInitializeCompleted = false + + /** + * Tracks whether [GodotLib.setup] was completed successfully. + */ + private var nativeLayerSetupCompleted = false + + /** + * Tracks whether [onInitRenderView] was completed successfully. + */ + private var renderViewInitialized = false + private var primaryHost: GodotHost? = null + + var io: GodotIO? = null + + private var commandLine : MutableList<String> = ArrayList<String>() + private var xrMode = XRMode.REGULAR + private var expansionPackPath: String = "" + private var useApkExpansion = false + private var useImmersive = false + private var useDebugOpengl = false + + private var containerLayout: FrameLayout? = null + var renderView: GodotRenderView? = null + + /** + * Returns true if the native engine has been initialized through [onInitNativeLayer], false otherwise. + */ + private fun isNativeInitialized() = nativeLayerInitializeCompleted && nativeLayerSetupCompleted + + /** + * Returns true if the engine has been initialized, false otherwise. + */ + fun isInitialized() = initializationStarted && isNativeInitialized() && renderViewInitialized + + /** + * Provides access to the primary host [Activity] + */ + fun getActivity() = primaryHost?.activity + private fun requireActivity() = getActivity() ?: throw IllegalStateException("Host activity must be non-null") + + /** + * Start initialization of the Godot engine. + * + * This must be followed by [onInitNativeLayer] and [onInitRenderView] in that order to complete + * initialization of the engine. + * + * @throws IllegalArgumentException exception if the specified expansion pack (if any) + * is invalid. + */ + fun onCreate(primaryHost: GodotHost) { + if (this.primaryHost != null || initializationStarted) { + Log.d(TAG, "OnCreate already invoked") + return + } + + beginBenchmarkMeasure("Godot::onCreate") + try { + this.primaryHost = primaryHost + val activity = requireActivity() + val window = activity.window + window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) + GodotPluginRegistry.initializePluginRegistry(this) + if (io == null) { + io = GodotIO(activity) + } + + // check for apk expansion API + commandLine = getCommandLine() + var mainPackMd5: String? = null + var mainPackKey: String? = null + val newArgs: MutableList<String> = ArrayList() + var i = 0 + while (i < commandLine.size) { + val hasExtra: Boolean = i < commandLine.size - 1 + if (commandLine[i] == XRMode.REGULAR.cmdLineArg) { + xrMode = XRMode.REGULAR + } else if (commandLine[i] == XRMode.OPENXR.cmdLineArg) { + xrMode = XRMode.OPENXR + } else if (commandLine[i] == "--debug_opengl") { + useDebugOpengl = true + } else if (commandLine[i] == "--use_immersive") { + useImmersive = true + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or // hide nav bar + View.SYSTEM_UI_FLAG_FULLSCREEN or // hide status bar + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + registerUiChangeListener() + } else if (commandLine[i] == "--use_apk_expansion") { + useApkExpansion = true + } else if (hasExtra && commandLine[i] == "--apk_expansion_md5") { + mainPackMd5 = commandLine[i + 1] + i++ + } else if (hasExtra && commandLine[i] == "--apk_expansion_key") { + mainPackKey = commandLine[i + 1] + val prefs = activity.getSharedPreferences( + "app_data_keys", + Context.MODE_PRIVATE + ) + val editor = prefs.edit() + editor.putString("store_public_key", mainPackKey) + editor.apply() + i++ + } else if (commandLine[i] == "--benchmark") { + useBenchmark = true + newArgs.add(commandLine[i]) + } else if (hasExtra && commandLine[i] == "--benchmark-file") { + useBenchmark = true + newArgs.add(commandLine[i]) + + // Retrieve the filepath + benchmarkFile = commandLine[i + 1] + newArgs.add(commandLine[i + 1]) + + i++ + } else if (commandLine[i].trim().isNotEmpty()) { + newArgs.add(commandLine[i]) + } + i++ + } + if (newArgs.isEmpty()) { + commandLine = mutableListOf() + } else { + commandLine = newArgs + } + if (useApkExpansion && mainPackMd5 != null && mainPackKey != null) { + // Build the full path to the app's expansion files + try { + expansionPackPath = Helpers.getSaveFilePath(context) + expansionPackPath += "/main." + activity.packageManager.getPackageInfo( + activity.packageName, + 0 + ).versionCode + "." + activity.packageName + ".obb" + } catch (e: java.lang.Exception) { + Log.e(TAG, "Unable to build full path to the app's expansion files", e) + } + val f = File(expansionPackPath) + var packValid = true + if (!f.exists()) { + packValid = false + } else if (obbIsCorrupted(expansionPackPath, mainPackMd5)) { + packValid = false + try { + f.delete() + } catch (_: java.lang.Exception) { + } + } + if (!packValid) { + // Aborting engine initialization + throw IllegalArgumentException("Invalid expansion pack") + } + } + + initializationStarted = true + } catch (e: java.lang.Exception) { + // Clear the primary host and rethrow + this.primaryHost = null + initializationStarted = false + throw e + } finally { + endBenchmarkMeasure("Godot::onCreate"); + } + } + + /** + * Initializes the native layer of the Godot engine. + * + * This must be preceded by [onCreate] and followed by [onInitRenderView] to complete + * initialization of the engine. + * + * @return false if initialization of the native layer fails, true otherwise. + * + * @throws IllegalStateException if [onCreate] has not been called. + */ + fun onInitNativeLayer(host: GodotHost): Boolean { + if (!initializationStarted) { + throw IllegalStateException("OnCreate must be invoked successfully prior to initializing the native layer") + } + if (isNativeInitialized()) { + Log.d(TAG, "OnInitNativeLayer already invoked") + return true + } + if (host != primaryHost) { + Log.e(TAG, "Native initialization is only supported for the primary host") + return false + } + + if (expansionPackPath.isNotEmpty()) { + commandLine.add("--main-pack") + commandLine.add(expansionPackPath) + } + val activity = requireActivity() + if (!nativeLayerInitializeCompleted) { + nativeLayerInitializeCompleted = GodotLib.initialize( + activity, + this, + activity.assets, + io, + netUtils, + directoryAccessHandler, + fileAccessHandler, + useApkExpansion, + ) + } + + if (nativeLayerInitializeCompleted && !nativeLayerSetupCompleted) { + nativeLayerSetupCompleted = GodotLib.setup(commandLine.toTypedArray(), tts) + if (!nativeLayerSetupCompleted) { + Log.e(TAG, "Unable to setup the Godot engine! Aborting...") + alert(R.string.error_engine_setup_message, R.string.text_error_title, this::forceQuit) + } + } + return isNativeInitialized() + } + + /** + * Used to complete initialization of the view used by the engine for rendering. + * + * This must be preceded by [onCreate] and [onInitNativeLayer] in that order to properly + * initialize the engine. + * + * @param host The [GodotHost] that's initializing the render views + * @param providedContainerLayout Optional argument; if provided, this is reused to host the Godot's render views + * + * @return A [FrameLayout] instance containing Godot's render views if initialization is successful, null otherwise. + * + * @throws IllegalStateException if [onInitNativeLayer] has not been called + */ + @JvmOverloads + fun onInitRenderView(host: GodotHost, providedContainerLayout: FrameLayout = FrameLayout(host.activity)): FrameLayout? { + if (!isNativeInitialized()) { + throw IllegalStateException("onInitNativeLayer() must be invoked successfully prior to initializing the render view") + } + + try { + val activity: Activity = host.activity + containerLayout = providedContainerLayout + containerLayout?.removeAllViews() + containerLayout?.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + + // GodotEditText layout + val editText = GodotEditText(activity) + editText.layoutParams = + ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + activity.resources.getDimension(R.dimen.text_edit_height).toInt() + ) + // ...add to FrameLayout + containerLayout?.addView(editText) + renderView = if (usesVulkan()) { + if (!meetsVulkanRequirements(activity.packageManager)) { + alert(R.string.error_missing_vulkan_requirements_message, R.string.text_error_title, this::forceQuit) + return null + } + GodotVulkanRenderView(host, this) + } else { + // Fallback to openGl + GodotGLRenderView(host, this, xrMode, useDebugOpengl) + } + if (host == primaryHost) { + renderView!!.startRenderer() + } + val view: View = renderView!!.view + containerLayout?.addView( + view, + ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + ) + editText.setView(renderView) + io?.setEdit(editText) + + // Listeners for keyboard height. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // Report the height of virtual keyboard as it changes during the animation. + val decorView = activity.window.decorView + decorView.setWindowInsetsAnimationCallback(object : WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { + var startBottom = 0 + var endBottom = 0 + override fun onPrepare(animation: WindowInsetsAnimation) { + startBottom = decorView.rootWindowInsets.getInsets(WindowInsets.Type.ime()).bottom + } + + override fun onStart(animation: WindowInsetsAnimation, bounds: WindowInsetsAnimation.Bounds): WindowInsetsAnimation.Bounds { + endBottom = decorView.rootWindowInsets.getInsets(WindowInsets.Type.ime()).bottom + return bounds + } + + override fun onProgress(windowInsets: WindowInsets, list: List<WindowInsetsAnimation>): WindowInsets { + // Find the IME animation. + var imeAnimation: WindowInsetsAnimation? = null + for (animation in list) { + if (animation.typeMask and WindowInsets.Type.ime() != 0) { + imeAnimation = animation + break + } + } + // Update keyboard height based on IME animation. + if (imeAnimation != null) { + val interpolatedFraction = imeAnimation.interpolatedFraction + // Linear interpolation between start and end values. + val keyboardHeight = startBottom * (1.0f - interpolatedFraction) + endBottom * interpolatedFraction + GodotLib.setVirtualKeyboardHeight(keyboardHeight.toInt()) + } + return windowInsets + } + + override fun onEnd(animation: WindowInsetsAnimation) {} + }) + } else { + // Infer the virtual keyboard height using visible area. + view.viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener { + // Don't allocate a new Rect every time the callback is called. + val visibleSize = Rect() + override fun onGlobalLayout() { + val surfaceView = renderView!!.view + surfaceView.getWindowVisibleDisplayFrame(visibleSize) + val keyboardHeight = surfaceView.height - visibleSize.bottom + GodotLib.setVirtualKeyboardHeight(keyboardHeight) + } + }) + } + + if (host == primaryHost) { + renderView!!.queueOnRenderThread { + for (plugin in pluginRegistry.allPlugins) { + plugin.onRegisterPluginWithGodotNative() + } + setKeepScreenOn(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on"))) + } + + // Include the returned non-null views in the Godot view hierarchy. + for (plugin in pluginRegistry.allPlugins) { + val pluginView = plugin.onMainCreate(activity) + if (pluginView != null) { + if (plugin.shouldBeOnTop()) { + containerLayout?.addView(pluginView) + } else { + containerLayout?.addView(pluginView, 0) + } + } + } + } + renderViewInitialized = true + } finally { + if (!renderViewInitialized) { + containerLayout?.removeAllViews() + containerLayout = null + } + } + return containerLayout + } + + fun onResume(host: GodotHost) { + if (host != primaryHost) { + return + } + + renderView!!.onActivityResumed() + mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME) + mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME) + mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME) + mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME) + if (useImmersive) { + val window = requireActivity().window + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or // hide nav bar + View.SYSTEM_UI_FLAG_FULLSCREEN or // hide status bar + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + } + for (plugin in pluginRegistry.allPlugins) { + plugin.onMainResume() + } + } + + fun onPause(host: GodotHost) { + if (host != primaryHost) { + return + } + + renderView!!.onActivityPaused() + mSensorManager.unregisterListener(this) + for (plugin in pluginRegistry.allPlugins) { + plugin.onMainPause() + } + } + + fun onDestroy(primaryHost: GodotHost) { + if (this.primaryHost != primaryHost) { + return + } + + for (plugin in pluginRegistry.allPlugins) { + plugin.onMainDestroy() + } + GodotLib.ondestroy() + forceQuit() + } + + /** + * Activity result callback + */ + fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + for (plugin in pluginRegistry.allPlugins) { + plugin.onMainActivityResult(requestCode, resultCode, data) + } + } + + /** + * Permissions request callback + */ + fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array<String?>, + grantResults: IntArray + ) { + for (plugin in pluginRegistry.allPlugins) { + plugin.onMainRequestPermissionsResult(requestCode, permissions, grantResults) + } + for (i in permissions.indices) { + GodotLib.requestPermissionResult( + permissions[i], + grantResults[i] == PackageManager.PERMISSION_GRANTED + ) + } + } + + /** + * Invoked on the render thread when the Godot setup is complete. + */ + private fun onGodotSetupCompleted() { + for (plugin in pluginRegistry.allPlugins) { + plugin.onGodotSetupCompleted() + } + primaryHost?.onGodotSetupCompleted() + } + + /** + * Invoked on the render thread when the Godot main loop has started. + */ + private fun onGodotMainLoopStarted() { + for (plugin in pluginRegistry.allPlugins) { + plugin.onGodotMainLoopStarted() + } + primaryHost?.onGodotMainLoopStarted() + } + + private fun restart() { + primaryHost?.onGodotRestartRequested(this) + } + + private fun registerUiChangeListener() { + val decorView = requireActivity().window.decorView + decorView.setOnSystemUiVisibilityChangeListener(uiChangeListener) + } + + @Keep + private fun alert(message: String, title: String) { + alert(message, title, null) + } + + private fun alert( + @StringRes messageResId: Int, + @StringRes titleResId: Int, + okCallback: Runnable? + ) { + val res: Resources = getActivity()?.resources ?: return + alert(res.getString(messageResId), res.getString(titleResId), okCallback) + } + + private fun alert(message: String, title: String, okCallback: Runnable?) { + val activity: Activity = getActivity() ?: return + runOnUiThread(Runnable { + val builder = AlertDialog.Builder(activity) + builder.setMessage(message).setTitle(title) + builder.setPositiveButton( + "OK" + ) { dialog: DialogInterface, id: Int -> + okCallback?.run() + dialog.cancel() + } + val dialog = builder.create() + dialog.show() + }) + } + + /** + * Queue a runnable to be run on the render thread. + * + * This must be called after the render thread has started. + */ + fun runOnRenderThread(action: Runnable) { + if (renderView != null) { + renderView!!.queueOnRenderThread(action) + } + } + + /** + * Runs the specified action on the UI thread. + * If the current thread is the UI thread, then the action is executed immediately. + * If the current thread is not the UI thread, the action is posted to the event queue + * of the UI thread. + */ + fun runOnUiThread(action: Runnable) { + val activity: Activity = getActivity() ?: return + activity.runOnUiThread(action) + } + + /** + * Returns true if the call is being made on the Ui thread. + */ + private fun isOnUiThread() = Looper.myLooper() == Looper.getMainLooper() + + /** + * Returns true if `Vulkan` is used for rendering. + */ + private fun usesVulkan(): Boolean { + val renderer = GodotLib.getGlobal("rendering/renderer/rendering_method") + val renderingDevice = GodotLib.getGlobal("rendering/rendering_device/driver") + return ("forward_plus" == renderer || "mobile" == renderer) && "vulkan" == renderingDevice + } + + /** + * Returns true if the device meets the base requirements for Vulkan support, false otherwise. + */ + private fun meetsVulkanRequirements(packageManager: PackageManager?): Boolean { + if (packageManager == null) { + return false + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (!packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL, 1)) { + // Optional requirements.. log as warning if missing + Log.w(TAG, "The vulkan hardware level does not meet the minimum requirement: 1") + } + + // Check for api version 1.0 + return packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x400003) + } + return false + } + + private fun setKeepScreenOn(p_enabled: Boolean) { + runOnUiThread { + if (p_enabled) { + getActivity()?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } else { + getActivity()?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + } + } + + fun hasClipboard(): Boolean { + return mClipboard.hasPrimaryClip() + } + + fun getClipboard(): String? { + val clipData = mClipboard.primaryClip ?: return "" + val text = clipData.getItemAt(0).text ?: return "" + return text.toString() + } + + fun setClipboard(text: String?) { + val clip = ClipData.newPlainText("myLabel", text) + mClipboard.setPrimaryClip(clip) + } + + private fun forceQuit() { + forceQuit(0) + } + + @Keep + private fun forceQuit(instanceId: Int): Boolean { + if (primaryHost == null) { + return false + } + return if (instanceId == 0) { + primaryHost!!.onGodotForceQuit(this) + true + } else { + primaryHost!!.onGodotForceQuit(instanceId) + } + } + + fun onBackPressed(host: GodotHost) { + if (host != primaryHost) { + return + } + + var shouldQuit = true + for (plugin in pluginRegistry.allPlugins) { + if (plugin.onMainBackPressed()) { + shouldQuit = false + } + } + if (shouldQuit && renderView != null) { + renderView!!.queueOnRenderThread { GodotLib.back() } + } + } + + private fun getRotatedValues(values: FloatArray?): FloatArray? { + if (values == null || values.size != 3) { + return values + } + val display = + (requireActivity().getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay + val displayRotation = display.rotation + val rotatedValues = FloatArray(3) + when (displayRotation) { + Surface.ROTATION_0 -> { + rotatedValues[0] = values[0] + rotatedValues[1] = values[1] + rotatedValues[2] = values[2] + } + Surface.ROTATION_90 -> { + rotatedValues[0] = -values[1] + rotatedValues[1] = values[0] + rotatedValues[2] = values[2] + } + Surface.ROTATION_180 -> { + rotatedValues[0] = -values[0] + rotatedValues[1] = -values[1] + rotatedValues[2] = values[2] + } + Surface.ROTATION_270 -> { + rotatedValues[0] = values[1] + rotatedValues[1] = -values[0] + rotatedValues[2] = values[2] + } + } + return rotatedValues + } + + override fun onSensorChanged(event: SensorEvent) { + if (renderView == null) { + return + } + when (event.sensor.type) { + Sensor.TYPE_ACCELEROMETER -> { + val rotatedValues = getRotatedValues(event.values) + renderView!!.queueOnRenderThread { + GodotLib.accelerometer( + -rotatedValues!![0], -rotatedValues[1], -rotatedValues[2] + ) + } + } + Sensor.TYPE_GRAVITY -> { + val rotatedValues = getRotatedValues(event.values) + renderView!!.queueOnRenderThread { + GodotLib.gravity( + -rotatedValues!![0], -rotatedValues[1], -rotatedValues[2] + ) + } + } + Sensor.TYPE_MAGNETIC_FIELD -> { + val rotatedValues = getRotatedValues(event.values) + renderView!!.queueOnRenderThread { + GodotLib.magnetometer( + -rotatedValues!![0], -rotatedValues[1], -rotatedValues[2] + ) + } + } + Sensor.TYPE_GYROSCOPE -> { + val rotatedValues = getRotatedValues(event.values) + renderView!!.queueOnRenderThread { + GodotLib.gyroscope( + rotatedValues!![0], rotatedValues[1], rotatedValues[2] + ) + } + } + } + } + + override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { + // Do something here if sensor accuracy changes. + } + + /** + * Used by the native code (java_godot_wrapper.h) to vibrate the device. + * @param durationMs + */ + @SuppressLint("MissingPermission") + @Keep + private fun vibrate(durationMs: Int) { + if (durationMs > 0 && requestPermission("VIBRATE")) { + val vibratorService = getActivity()?.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator? ?: return + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + vibratorService.vibrate( + VibrationEffect.createOneShot( + durationMs.toLong(), + VibrationEffect.DEFAULT_AMPLITUDE + ) + ) + } else { + // deprecated in API 26 + vibratorService.vibrate(durationMs.toLong()) + } + } + } + + private fun getCommandLine(): MutableList<String> { + val original: MutableList<String> = parseCommandLine() + val hostCommandLine = primaryHost?.commandLine + if (hostCommandLine != null && hostCommandLine.isNotEmpty()) { + original.addAll(hostCommandLine) + } + return original + } + + private fun parseCommandLine(): MutableList<String> { + val inputStream: InputStream + return try { + inputStream = requireActivity().assets.open("_cl_") + val len = ByteArray(4) + var r = inputStream.read(len) + if (r < 4) { + return mutableListOf() + } + val argc = + (len[3].toInt() and 0xFF) shl 24 or ((len[2].toInt() and 0xFF) shl 16) or ((len[1].toInt() and 0xFF) shl 8) or (len[0].toInt() and 0xFF) + val cmdline = ArrayList<String>(argc) + for (i in 0 until argc) { + r = inputStream.read(len) + if (r < 4) { + return mutableListOf() + } + val strlen = + (len[3].toInt() and 0xFF) shl 24 or ((len[2].toInt() and 0xFF) shl 16) or ((len[1].toInt() and 0xFF) shl 8) or (len[0].toInt() and 0xFF) + if (strlen > 65535) { + return mutableListOf() + } + val arg = ByteArray(strlen) + r = inputStream.read(arg) + if (r == strlen) { + cmdline[i] = String(arg, StandardCharsets.UTF_8) + } + } + cmdline + } catch (e: Exception) { + // The _cl_ file can be missing with no adverse effect + mutableListOf() + } + } + + /** + * Used by the native code (java_godot_wrapper.h) to access the input fallback mapping. + * @return The input fallback mapping for the current XR mode. + */ + @Keep + private fun getInputFallbackMapping(): String? { + return xrMode.inputFallbackMapping + } + + fun requestPermission(name: String?): Boolean { + return requestPermission(name, getActivity()) + } + + fun requestPermissions(): Boolean { + return PermissionsUtil.requestManifestPermissions(getActivity()) + } + + fun getGrantedPermissions(): Array<String?>? { + return PermissionsUtil.getGrantedPermissions(getActivity()) + } + + @Keep + private fun getCACertificates(): String { + return GodotNetUtils.getCACertificates() + } + + private fun obbIsCorrupted(f: String, mainPackMd5: String): Boolean { + return try { + val fis: InputStream = FileInputStream(f) + + // Create MD5 Hash + val buffer = ByteArray(16384) + val complete = MessageDigest.getInstance("MD5") + var numRead: Int + do { + numRead = fis.read(buffer) + if (numRead > 0) { + complete.update(buffer, 0, numRead) + } + } while (numRead != -1) + fis.close() + val messageDigest = complete.digest() + + // Create Hex String + val hexString = StringBuilder() + for (b in messageDigest) { + var s = Integer.toHexString(0xFF and b.toInt()) + if (s.length == 1) { + s = "0$s" + } + hexString.append(s) + } + val md5str = hexString.toString() + md5str != mainPackMd5 + } catch (e: java.lang.Exception) { + e.printStackTrace() + true + } + } + + @Keep + private fun initInputDevices() { + renderView!!.initInputDevices() + } + + @Keep + private fun createNewGodotInstance(args: Array<String>): Int { + return primaryHost?.onNewGodotInstanceRequested(args) ?: 0 + } + + @Keep + private fun nativeBeginBenchmarkMeasure(label: String) { + beginBenchmarkMeasure(label) + } + + @Keep + private fun nativeEndBenchmarkMeasure(label: String) { + endBenchmarkMeasure(label) + } + + @Keep + private fun nativeDumpBenchmark(benchmarkFile: String) { + dumpBenchmark(fileAccessHandler, benchmarkFile) + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt new file mode 100644 index 0000000000..4636f753af --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt @@ -0,0 +1,167 @@ +/**************************************************************************/ +/* GodotActivity.kt */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +package org.godotengine.godot + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.util.Log +import androidx.annotation.CallSuper +import androidx.fragment.app.FragmentActivity +import org.godotengine.godot.utils.ProcessPhoenix + +/** + * Base abstract activity for Android apps intending to use Godot as the primary screen. + * + * Also a reference implementation for how to setup and use the [GodotFragment] fragment + * within an Android app. + */ +abstract class GodotActivity : FragmentActivity(), GodotHost { + + companion object { + private val TAG = GodotActivity::class.java.simpleName + + @JvmStatic + protected val EXTRA_FORCE_QUIT = "force_quit_requested" + @JvmStatic + protected val EXTRA_NEW_LAUNCH = "new_launch_requested" + } + + /** + * Interaction with the [Godot] object is delegated to the [GodotFragment] class. + */ + protected var godotFragment: GodotFragment? = null + private set + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.godot_app_layout) + + handleStartIntent(intent, true) + + val currentFragment = supportFragmentManager.findFragmentById(R.id.godot_fragment_container) + if (currentFragment is GodotFragment) { + Log.v(TAG, "Reusing existing Godot fragment instance.") + godotFragment = currentFragment + } else { + Log.v(TAG, "Creating new Godot fragment instance.") + godotFragment = initGodotInstance() + supportFragmentManager.beginTransaction().replace(R.id.godot_fragment_container, godotFragment!!).setPrimaryNavigationFragment(godotFragment).commitNowAllowingStateLoss() + } + } + + override fun onDestroy() { + Log.v(TAG, "Destroying Godot app...") + super.onDestroy() + if (godotFragment != null) { + terminateGodotInstance(godotFragment!!.godot) + } + } + + override fun onGodotForceQuit(instance: Godot) { + runOnUiThread { terminateGodotInstance(instance) } + } + + private fun terminateGodotInstance(instance: Godot) { + if (godotFragment != null && instance === godotFragment!!.godot) { + Log.v(TAG, "Force quitting Godot instance") + ProcessPhoenix.forceQuit(this) + } + } + + override fun onGodotRestartRequested(instance: Godot) { + runOnUiThread { + if (godotFragment != null && instance === godotFragment!!.godot) { + // It's very hard to properly de-initialize Godot on Android to restart the game + // from scratch. Therefore, we need to kill the whole app process and relaunch it. + // + // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including + // releasing and reloading native libs or resetting their state somehow and clearing static data). + Log.v(TAG, "Restarting Godot instance...") + ProcessPhoenix.triggerRebirth(this) + } + } + } + + override fun onNewIntent(newIntent: Intent) { + super.onNewIntent(newIntent) + intent = newIntent + + handleStartIntent(newIntent, false) + + godotFragment?.onNewIntent(newIntent) + } + + private fun handleStartIntent(intent: Intent, newLaunch: Boolean) { + val forceQuitRequested = intent.getBooleanExtra(EXTRA_FORCE_QUIT, false) + if (forceQuitRequested) { + Log.d(TAG, "Force quit requested, terminating..") + ProcessPhoenix.forceQuit(this) + return + } + if (!newLaunch) { + val newLaunchRequested = intent.getBooleanExtra(EXTRA_NEW_LAUNCH, false) + if (newLaunchRequested) { + Log.d(TAG, "New launch requested, restarting..") + val restartIntent = Intent(intent).putExtra(EXTRA_NEW_LAUNCH, false) + ProcessPhoenix.triggerRebirth(this, restartIntent) + return + } + } + } + + @CallSuper + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + godotFragment?.onActivityResult(requestCode, resultCode, data) + } + + @CallSuper + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + godotFragment?.onRequestPermissionsResult(requestCode, permissions, grantResults) + } + + override fun onBackPressed() { + godotFragment?.onBackPressed() ?: super.onBackPressed() + } + + override fun getActivity(): Activity? { + return this + } + + /** + * Used to initialize the Godot fragment instance in [onCreate]. + */ + protected open fun initGodotInstance(): GodotFragment { + return GodotFragment() + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java new file mode 100644 index 0000000000..9a8b10ea3e --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* GodotFragment.java */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +package org.godotengine.godot; + +import org.godotengine.godot.utils.BenchmarkUtils; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Messenger; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import androidx.annotation.CallSuper; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.google.android.vending.expansion.downloader.DownloadProgressInfo; +import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; +import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller; +import com.google.android.vending.expansion.downloader.Helpers; +import com.google.android.vending.expansion.downloader.IDownloaderClient; +import com.google.android.vending.expansion.downloader.IDownloaderService; +import com.google.android.vending.expansion.downloader.IStub; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +/** + * Base fragment for Android apps intending to use Godot for part of the app's UI. + */ +public class GodotFragment extends Fragment implements IDownloaderClient, GodotHost { + private static final String TAG = GodotFragment.class.getSimpleName(); + + private IStub mDownloaderClientStub; + private TextView mStatusText; + private TextView mProgressFraction; + private TextView mProgressPercent; + private TextView mAverageSpeed; + private TextView mTimeRemaining; + private ProgressBar mPB; + + private View mDashboard; + private View mCellMessage; + + private Button mPauseButton; + private Button mWiFiSettingsButton; + + private FrameLayout godotContainerLayout; + private boolean mStatePaused; + private int mState; + + @Nullable + private GodotHost parentHost; + private Godot godot; + + static private Intent mCurrentIntent; + + public void onNewIntent(Intent intent) { + mCurrentIntent = intent; + } + + static public Intent getCurrentIntent() { + return mCurrentIntent; + } + + private void setState(int newState) { + if (mState != newState) { + mState = newState; + mStatusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState)); + } + } + + private void setButtonPausedState(boolean paused) { + mStatePaused = paused; + int stringResourceID = paused ? R.string.text_button_resume : R.string.text_button_pause; + mPauseButton.setText(stringResourceID); + } + + public interface ResultCallback { + void callback(int requestCode, int resultCode, Intent data); + } + public ResultCallback resultCallback; + + public Godot getGodot() { + return godot; + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + if (getParentFragment() instanceof GodotHost) { + parentHost = (GodotHost)getParentFragment(); + } else if (getActivity() instanceof GodotHost) { + parentHost = (GodotHost)getActivity(); + } + } + + @Override + public void onDetach() { + super.onDetach(); + parentHost = null; + } + + @CallSuper + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCallback != null) { + resultCallback.callback(requestCode, resultCode, data); + resultCallback = null; + } + + godot.onActivityResult(requestCode, resultCode, data); + } + + @CallSuper + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + godot.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + @Override + public void onServiceConnected(Messenger m) { + IDownloaderService remoteService = DownloaderServiceMarshaller.CreateProxy(m); + remoteService.onClientUpdated(mDownloaderClientStub.getMessenger()); + } + + @Override + public void onCreate(Bundle icicle) { + BenchmarkUtils.beginBenchmarkMeasure("GodotFragment::onCreate"); + super.onCreate(icicle); + + final Activity activity = getActivity(); + mCurrentIntent = activity.getIntent(); + + godot = new Godot(requireContext()); + performEngineInitialization(); + BenchmarkUtils.endBenchmarkMeasure("GodotFragment::onCreate"); + } + + private void performEngineInitialization() { + try { + godot.onCreate(this); + + if (!godot.onInitNativeLayer(this)) { + throw new IllegalStateException("Unable to initialize engine native layer"); + } + + godotContainerLayout = godot.onInitRenderView(this); + if (godotContainerLayout == null) { + throw new IllegalStateException("Unable to initialize engine render view"); + } + } catch (IllegalArgumentException ignored) { + final Activity activity = getActivity(); + Intent notifierIntent = new Intent(activity, activity.getClass()); + notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + + PendingIntent pendingIntent; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + pendingIntent = PendingIntent.getActivity(activity, 0, + notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + } else { + pendingIntent = PendingIntent.getActivity(activity, 0, + notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); + } + + int startResult; + try { + startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(getContext(), pendingIntent, GodotDownloaderService.class); + + if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { + // This is where you do set up to display the download + // progress (next step in onCreateView) + mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this, GodotDownloaderService.class); + return; + } + + // Restart engine initialization + performEngineInitialization(); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Unable to start download service", e); + } + } + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle icicle) { + if (mDownloaderClientStub != null) { + View downloadingExpansionView = + inflater.inflate(R.layout.downloading_expansion, container, false); + mPB = (ProgressBar)downloadingExpansionView.findViewById(R.id.progressBar); + mStatusText = (TextView)downloadingExpansionView.findViewById(R.id.statusText); + mProgressFraction = (TextView)downloadingExpansionView.findViewById(R.id.progressAsFraction); + mProgressPercent = (TextView)downloadingExpansionView.findViewById(R.id.progressAsPercentage); + mAverageSpeed = (TextView)downloadingExpansionView.findViewById(R.id.progressAverageSpeed); + mTimeRemaining = (TextView)downloadingExpansionView.findViewById(R.id.progressTimeRemaining); + mDashboard = downloadingExpansionView.findViewById(R.id.downloaderDashboard); + mCellMessage = downloadingExpansionView.findViewById(R.id.approveCellular); + mPauseButton = (Button)downloadingExpansionView.findViewById(R.id.pauseButton); + mWiFiSettingsButton = (Button)downloadingExpansionView.findViewById(R.id.wifiSettingsButton); + + return downloadingExpansionView; + } + + return godotContainerLayout; + } + + @Override + public void onDestroy() { + godot.onDestroy(this); + super.onDestroy(); + } + + @Override + public void onPause() { + super.onPause(); + + if (!godot.isInitialized()) { + if (null != mDownloaderClientStub) { + mDownloaderClientStub.disconnect(getActivity()); + } + return; + } + + godot.onPause(this); + } + + @Override + public void onResume() { + super.onResume(); + if (!godot.isInitialized()) { + if (null != mDownloaderClientStub) { + mDownloaderClientStub.connect(getActivity()); + } + return; + } + + godot.onResume(this); + } + + public void onBackPressed() { + godot.onBackPressed(this); + } + + /** + * The download state should trigger changes in the UI --- it may be useful + * to show the state as being indeterminate at times. This sample can be + * considered a guideline. + */ + @Override + public void onDownloadStateChanged(int newState) { + setState(newState); + boolean showDashboard = true; + boolean showCellMessage = false; + boolean paused; + boolean indeterminate; + switch (newState) { + case IDownloaderClient.STATE_IDLE: + // STATE_IDLE means the service is listening, so it's + // safe to start making remote service calls. + paused = false; + indeterminate = true; + break; + case IDownloaderClient.STATE_CONNECTING: + case IDownloaderClient.STATE_FETCHING_URL: + showDashboard = true; + paused = false; + indeterminate = true; + break; + case IDownloaderClient.STATE_DOWNLOADING: + paused = false; + showDashboard = true; + indeterminate = false; + break; + + case IDownloaderClient.STATE_FAILED_CANCELED: + case IDownloaderClient.STATE_FAILED: + case IDownloaderClient.STATE_FAILED_FETCHING_URL: + case IDownloaderClient.STATE_FAILED_UNLICENSED: + paused = true; + showDashboard = false; + indeterminate = false; + break; + case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION: + case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION: + showDashboard = false; + paused = true; + indeterminate = false; + showCellMessage = true; + break; + + case IDownloaderClient.STATE_PAUSED_BY_REQUEST: + paused = true; + indeterminate = false; + break; + case IDownloaderClient.STATE_PAUSED_ROAMING: + case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE: + paused = true; + indeterminate = false; + break; + case IDownloaderClient.STATE_COMPLETED: + showDashboard = false; + paused = false; + indeterminate = false; + performEngineInitialization(); + return; + default: + paused = true; + indeterminate = true; + showDashboard = true; + } + int newDashboardVisibility = showDashboard ? View.VISIBLE : View.GONE; + if (mDashboard.getVisibility() != newDashboardVisibility) { + mDashboard.setVisibility(newDashboardVisibility); + } + int cellMessageVisibility = showCellMessage ? View.VISIBLE : View.GONE; + if (mCellMessage.getVisibility() != cellMessageVisibility) { + mCellMessage.setVisibility(cellMessageVisibility); + } + + mPB.setIndeterminate(indeterminate); + setButtonPausedState(paused); + } + + @Override + public void onDownloadProgress(DownloadProgressInfo progress) { + mAverageSpeed.setText(getString(R.string.kilobytes_per_second, + Helpers.getSpeedString(progress.mCurrentSpeed))); + mTimeRemaining.setText(getString(R.string.time_remaining, + Helpers.getTimeRemaining(progress.mTimeRemaining))); + + mPB.setMax((int)(progress.mOverallTotal >> 8)); + mPB.setProgress((int)(progress.mOverallProgress >> 8)); + mProgressPercent.setText(String.format(Locale.ENGLISH, "%d %%", progress.mOverallProgress * 100 / progress.mOverallTotal)); + mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress, + progress.mOverallTotal)); + } + + @CallSuper + @Override + public List<String> getCommandLine() { + return parentHost != null ? parentHost.getCommandLine() : Collections.emptyList(); + } + + @CallSuper + @Override + public void onGodotSetupCompleted() { + if (parentHost != null) { + parentHost.onGodotSetupCompleted(); + } + } + + @CallSuper + @Override + public void onGodotMainLoopStarted() { + if (parentHost != null) { + parentHost.onGodotMainLoopStarted(); + } + } + + @Override + public void onGodotForceQuit(Godot instance) { + if (parentHost != null) { + parentHost.onGodotForceQuit(instance); + } + } + + @Override + public boolean onGodotForceQuit(int godotInstanceId) { + return parentHost != null && parentHost.onGodotForceQuit(godotInstanceId); + } + + @Override + public void onGodotRestartRequested(Godot instance) { + if (parentHost != null) { + parentHost.onGodotRestartRequested(instance); + } + } + + @Override + public int onNewGodotInstanceRequested(String[] args) { + if (parentHost != null) { + return parentHost.onNewGodotInstanceRequested(args); + } + return 0; + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index b465377743..52350c12a6 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -29,10 +29,10 @@ /**************************************************************************/ package org.godotengine.godot; + import org.godotengine.godot.gl.GLSurfaceView; import org.godotengine.godot.gl.GodotRenderer; import org.godotengine.godot.input.GodotInputHandler; -import org.godotengine.godot.utils.GLUtils; import org.godotengine.godot.xr.XRMode; import org.godotengine.godot.xr.ovr.OvrConfigChooser; import org.godotengine.godot.xr.ovr.OvrContextFactory; @@ -78,22 +78,23 @@ import java.io.InputStream; * bit depths). Failure to do so would result in an EGL_BAD_MATCH error. */ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView { + private final GodotHost host; private final Godot godot; private final GodotInputHandler inputHandler; private final GodotRenderer godotRenderer; private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>(); - public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_debug_opengl) { - super(context); - GLUtils.use_debug_opengl = p_use_debug_opengl; + public GodotGLRenderView(GodotHost host, Godot godot, XRMode xrMode, boolean useDebugOpengl) { + super(host.getActivity()); + this.host = host; this.godot = godot; this.inputHandler = new GodotInputHandler(this); this.godotRenderer = new GodotRenderer(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT)); } - init(xrMode, false); + init(xrMode, false, useDebugOpengl); } @Override @@ -123,7 +124,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView @Override public void onBackPressed() { - godot.onBackPressed(); + godot.onBackPressed(host); } @Override @@ -233,7 +234,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView return super.onResolvePointerIcon(me, pointerIndex); } - private void init(XRMode xrMode, boolean translucent) { + private void init(XRMode xrMode, boolean translucent, boolean useDebugOpengl) { setPreserveEGLContextOnPause(true); setFocusableInTouchMode(true); switch (xrMode) { @@ -262,7 +263,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView /* Setup the context factory for 2.0 rendering. * See ContextFactory class definition below */ - setEGLContextFactory(new RegularContextFactory()); + setEGLContextFactory(new RegularContextFactory(useDebugOpengl)); /* We need to choose an EGLConfig that matches the format of * our surface exactly. This is going to be done in our @@ -275,7 +276,10 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView new RegularConfigChooser(8, 8, 8, 8, 16, 0))); break; } + } + @Override + public void startRenderer() { /* Set the renderer responsible for frame rendering */ setRenderer(godotRenderer); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java index 7700b9b628..e5333085dd 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java @@ -30,11 +30,13 @@ package org.godotengine.godot; +import android.app.Activity; + import java.util.Collections; import java.util.List; /** - * Denotate a component (e.g: Activity, Fragment) that hosts the {@link Godot} fragment. + * Denotate a component (e.g: Activity, Fragment) that hosts the {@link Godot} engine. */ public interface GodotHost { /** @@ -86,4 +88,9 @@ public interface GodotHost { default int onNewGodotInstanceRequested(String[] args) { return 0; } + + /** + * Provide access to the Activity hosting the Godot engine. + */ + Activity getActivity(); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java index 00243dab2a..ebf3a6b2fb 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java @@ -39,6 +39,11 @@ public interface GodotRenderView { void initInputDevices(); + /** + * Starts the thread that will drive Godot's rendering. + */ + void startRenderer(); + void queueOnRenderThread(Runnable event); void onActivityPaused(); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt b/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt new file mode 100644 index 0000000000..68cd2c1358 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt @@ -0,0 +1,54 @@ +package org.godotengine.godot + +import android.app.Service +import android.content.Intent +import android.os.Binder +import android.os.IBinder +import android.util.Log + +/** + * Godot service responsible for hosting the Godot engine instance. + */ +class GodotService : Service() { + + companion object { + private val TAG = GodotService::class.java.simpleName + } + + private var boundIntent: Intent? = null + private val godot by lazy { + Godot(applicationContext) + } + + override fun onCreate() { + super.onCreate() + } + + override fun onDestroy() { + super.onDestroy() + } + + override fun onBind(intent: Intent?): IBinder? { + if (boundIntent != null) { + Log.d(TAG, "GodotService already bound") + return null + } + + boundIntent = intent + return GodotHandle(godot) + } + + override fun onRebind(intent: Intent?) { + super.onRebind(intent) + } + + override fun onUnbind(intent: Intent?): Boolean { + return super.onUnbind(intent) + } + + override fun onTaskRemoved(rootIntent: Intent?) { + super.onTaskRemoved(rootIntent) + } + + class GodotHandle(val godot: Godot) : Binder() +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java index 681e182adb..48708152be 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java @@ -35,7 +35,6 @@ import org.godotengine.godot.vulkan.VkRenderer; import org.godotengine.godot.vulkan.VkSurfaceView; import android.annotation.SuppressLint; -import android.content.Context; import android.content.res.AssetManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -52,14 +51,16 @@ import androidx.annotation.Keep; import java.io.InputStream; public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { + private final GodotHost host; private final Godot godot; private final GodotInputHandler mInputHandler; private final VkRenderer mRenderer; private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>(); - public GodotVulkanRenderView(Context context, Godot godot) { - super(context); + public GodotVulkanRenderView(GodotHost host, Godot godot) { + super(host.getActivity()); + this.host = host; this.godot = godot; mInputHandler = new GodotInputHandler(this); mRenderer = new VkRenderer(); @@ -67,6 +68,10 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT)); } setFocusableInTouchMode(true); + } + + @Override + public void startRenderer() { startRenderer(mRenderer); } @@ -97,7 +102,7 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV @Override public void onBackPressed() { - godot.onBackPressed(); + godot.onBackPressed(host); } @Override diff --git a/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java b/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java index edace53e7f..dce6753b7a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java +++ b/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java @@ -33,6 +33,7 @@ package org.godotengine.godot.tts; import org.godotengine.godot.GodotLib; import android.app.Activity; +import android.content.Context; import android.os.Bundle; import android.speech.tts.TextToSpeech; import android.speech.tts.UtteranceProgressListener; @@ -62,7 +63,7 @@ public class GodotTTS extends UtteranceProgressListener { final private static int EVENT_CANCEL = 2; final private static int EVENT_BOUNDARY = 3; - final private Activity activity; + private final Context context; private TextToSpeech synth; private LinkedList<GodotUtterance> queue; final private Object lock = new Object(); @@ -71,8 +72,8 @@ public class GodotTTS extends UtteranceProgressListener { private boolean speaking; private boolean paused; - public GodotTTS(Activity p_activity) { - activity = p_activity; + public GodotTTS(Context context) { + this.context = context; } private void updateTTS() { @@ -188,7 +189,7 @@ public class GodotTTS extends UtteranceProgressListener { * Initialize synth and query. */ public void init() { - synth = new TextToSpeech(activity, null); + synth = new TextToSpeech(context, null); queue = new LinkedList<GodotUtterance>(); synth.setOnUtteranceProgressListener(this); diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java index 7db02968bb..2c7b73ae4d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java @@ -44,8 +44,6 @@ public class GLUtils { public static final boolean DEBUG = false; - public static boolean use_debug_opengl = false; - private static final String[] ATTRIBUTES_NAMES = new String[] { "EGL_BUFFER_SIZE", "EGL_ALPHA_SIZE", diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java index c31d56a3e1..dca190a2fc 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java @@ -36,7 +36,8 @@ import android.net.wifi.WifiManager; import android.util.Base64; import android.util.Log; -import java.io.StringWriter; +import androidx.annotation.NonNull; + import java.security.KeyStore; import java.security.cert.X509Certificate; import java.util.Enumeration; @@ -50,9 +51,9 @@ public class GodotNetUtils { /* A single, reference counted, multicast lock, or null if permission CHANGE_WIFI_MULTICAST_STATE is missing */ private WifiManager.MulticastLock multicastLock; - public GodotNetUtils(Activity p_activity) { - if (PermissionsUtil.hasManifestPermission(p_activity, "android.permission.CHANGE_WIFI_MULTICAST_STATE")) { - WifiManager wifi = (WifiManager)p_activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + public GodotNetUtils(Context context) { + if (PermissionsUtil.hasManifestPermission(context, "android.permission.CHANGE_WIFI_MULTICAST_STATE")) { + WifiManager wifi = (WifiManager)context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); multicastLock = wifi.createMulticastLock("GodotMulticastLock"); multicastLock.setReferenceCounted(true); } @@ -91,7 +92,7 @@ public class GodotNetUtils { * @see https://developer.android.com/reference/java/security/KeyStore . * @return A string of concatenated X509 certificates in PEM format. */ - public static String getCACertificates() { + public static @NonNull String getCACertificates() { try { KeyStore ks = KeyStore.getInstance("AndroidCAStore"); StringBuilder writer = new StringBuilder(); diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java index a94188c405..8353fc8dc6 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java @@ -32,6 +32,7 @@ package org.godotengine.godot.utils; import android.Manifest; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -52,7 +53,6 @@ import java.util.Set; /** * This class includes utility functions for Android permissions related operations. */ - public final class PermissionsUtil { private static final String TAG = PermissionsUtil.class.getSimpleName(); @@ -193,13 +193,13 @@ public final class PermissionsUtil { /** * With this function you can get the list of dangerous permissions that have been granted to the Android application. - * @param activity the caller activity for this method. + * @param context the caller context for this method. * @return granted permissions list */ - public static String[] getGrantedPermissions(Activity activity) { + public static String[] getGrantedPermissions(Context context) { String[] manifestPermissions; try { - manifestPermissions = getManifestPermissions(activity); + manifestPermissions = getManifestPermissions(context); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return new String[0]; @@ -215,9 +215,9 @@ public final class PermissionsUtil { grantedPermissions.add(manifestPermission); } } else { - PermissionInfo permissionInfo = getPermissionInfo(activity, manifestPermission); + PermissionInfo permissionInfo = getPermissionInfo(context, manifestPermission); int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel; - if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, manifestPermission) == PackageManager.PERMISSION_GRANTED) { + if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(context, manifestPermission) == PackageManager.PERMISSION_GRANTED) { grantedPermissions.add(manifestPermission); } } @@ -232,13 +232,13 @@ public final class PermissionsUtil { /** * Check if the given permission is in the AndroidManifest.xml file. - * @param activity the caller activity for this method. + * @param context the caller context for this method. * @param permission the permession to look for in the manifest file. * @return "true" if the permission is in the manifest file of the activity, "false" otherwise. */ - public static boolean hasManifestPermission(Activity activity, String permission) { + public static boolean hasManifestPermission(Context context, String permission) { try { - for (String p : getManifestPermissions(activity)) { + for (String p : getManifestPermissions(context)) { if (permission.equals(p)) return true; } @@ -250,13 +250,13 @@ public final class PermissionsUtil { /** * Returns the permissions defined in the AndroidManifest.xml file. - * @param activity the caller activity for this method. + * @param context the caller context for this method. * @return manifest permissions list * @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found. */ - private static String[] getManifestPermissions(Activity activity) throws PackageManager.NameNotFoundException { - PackageManager packageManager = activity.getPackageManager(); - PackageInfo packageInfo = packageManager.getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS); + private static String[] getManifestPermissions(Context context) throws PackageManager.NameNotFoundException { + PackageManager packageManager = context.getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS); if (packageInfo.requestedPermissions == null) return new String[0]; return packageInfo.requestedPermissions; @@ -264,13 +264,13 @@ public final class PermissionsUtil { /** * Returns the information of the desired permission. - * @param activity the caller activity for this method. + * @param context the caller context for this method. * @param permission the name of the permission. * @return permission info object * @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found. */ - private static PermissionInfo getPermissionInfo(Activity activity, String permission) throws PackageManager.NameNotFoundException { - PackageManager packageManager = activity.getPackageManager(); + private static PermissionInfo getPermissionInfo(Context context, String permission) throws PackageManager.NameNotFoundException { + PackageManager packageManager = context.getPackageManager(); return packageManager.getPermissionInfo(permission, 0); } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java index 1a126ff765..01ee41e30b 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java @@ -51,12 +51,22 @@ public class RegularContextFactory implements GLSurfaceView.EGLContextFactory { private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + private final boolean mUseDebugOpengl; + + public RegularContextFactory() { + this(false); + } + + public RegularContextFactory(boolean useDebugOpengl) { + this.mUseDebugOpengl = useDebugOpengl; + } + public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { Log.w(TAG, "creating OpenGL ES 3.0 context :"); GLUtils.checkEglError(TAG, "Before eglCreateContext", egl); EGLContext context; - if (GLUtils.use_debug_opengl) { + if (mUseDebugOpengl) { int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 3, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); } else { diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index b54491e0e1..74605e3377 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -135,7 +135,7 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv os_android = new OS_Android(godot_java, godot_io_java, p_use_apk_expansion); - return godot_java->on_video_init(env); + return true; } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz) { diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 862d9f0436..79ba2528ba 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -58,12 +58,10 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ } // get some Godot method pointers... - _on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()Z"); _restart = p_env->GetMethodID(godot_class, "restart", "()V"); _finish = p_env->GetMethodID(godot_class, "forceQuit", "(I)Z"); _set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V"); _alert = p_env->GetMethodID(godot_class, "alert", "(Ljava/lang/String;Ljava/lang/String;)V"); - _get_GLES_version_code = p_env->GetMethodID(godot_class, "getGLESVersionCode", "()I"); _get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;"); _set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V"); _has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z"); @@ -72,20 +70,15 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;"); _get_ca_certificates = p_env->GetMethodID(godot_class, "getCACertificates", "()Ljava/lang/String;"); _init_input_devices = p_env->GetMethodID(godot_class, "initInputDevices", "()V"); - _get_surface = p_env->GetMethodID(godot_class, "getSurface", "()Landroid/view/Surface;"); - _is_activity_resumed = p_env->GetMethodID(godot_class, "isActivityResumed", "()Z"); _vibrate = p_env->GetMethodID(godot_class, "vibrate", "(I)V"); _get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;"); _on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V"); _on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V"); _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)I"); _get_render_view = p_env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;"); - _begin_benchmark_measure = p_env->GetMethodID(godot_class, "beginBenchmarkMeasure", "(Ljava/lang/String;)V"); - _end_benchmark_measure = p_env->GetMethodID(godot_class, "endBenchmarkMeasure", "(Ljava/lang/String;)V"); - _dump_benchmark = p_env->GetMethodID(godot_class, "dumpBenchmark", "(Ljava/lang/String;)V"); - - // get some Activity method pointers... - _get_class_loader = p_env->GetMethodID(activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;"); + _begin_benchmark_measure = p_env->GetMethodID(godot_class, "nativeBeginBenchmarkMeasure", "(Ljava/lang/String;)V"); + _end_benchmark_measure = p_env->GetMethodID(godot_class, "nativeEndBenchmarkMeasure", "(Ljava/lang/String;)V"); + _dump_benchmark = p_env->GetMethodID(godot_class, "nativeDumpBenchmark", "(Ljava/lang/String;)V"); } GodotJavaWrapper::~GodotJavaWrapper() { @@ -105,29 +98,6 @@ jobject GodotJavaWrapper::get_activity() { return activity; } -jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env) { - if (godot_class) { - if (p_env == nullptr) { - p_env = get_jni_env(); - } - ERR_FAIL_NULL_V(p_env, nullptr); - jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class); - return p_env->GetStaticObjectField(godot_class, fid); - } else { - return nullptr; - } -} - -jobject GodotJavaWrapper::get_class_loader() { - if (_get_class_loader) { - JNIEnv *env = get_jni_env(); - ERR_FAIL_NULL_V(env, nullptr); - return env->CallObjectMethod(activity, _get_class_loader); - } else { - return nullptr; - } -} - GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() { if (godot_view != nullptr) { return godot_view; @@ -143,17 +113,6 @@ GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() { return godot_view; } -bool GodotJavaWrapper::on_video_init(JNIEnv *p_env) { - if (_on_video_init) { - if (p_env == nullptr) { - p_env = get_jni_env(); - } - ERR_FAIL_NULL_V(p_env, false); - return p_env->CallBooleanMethod(godot_instance, _on_video_init); - } - return false; -} - void GodotJavaWrapper::on_godot_setup_completed(JNIEnv *p_env) { if (_on_godot_setup_completed) { if (p_env == nullptr) { @@ -212,15 +171,6 @@ void GodotJavaWrapper::alert(const String &p_message, const String &p_title) { } } -int GodotJavaWrapper::get_gles_version_code() { - JNIEnv *env = get_jni_env(); - ERR_FAIL_NULL_V(env, 0); - if (_get_GLES_version_code) { - return env->CallIntMethod(godot_instance, _get_GLES_version_code); - } - return 0; -} - bool GodotJavaWrapper::has_get_clipboard() { return _get_clipboard != nullptr; } @@ -333,26 +283,6 @@ void GodotJavaWrapper::init_input_devices() { } } -jobject GodotJavaWrapper::get_surface() { - if (_get_surface) { - JNIEnv *env = get_jni_env(); - ERR_FAIL_NULL_V(env, nullptr); - return env->CallObjectMethod(godot_instance, _get_surface); - } else { - return nullptr; - } -} - -bool GodotJavaWrapper::is_activity_resumed() { - if (_is_activity_resumed) { - JNIEnv *env = get_jni_env(); - ERR_FAIL_NULL_V(env, false); - return env->CallBooleanMethod(godot_instance, _is_activity_resumed); - } else { - return false; - } -} - void GodotJavaWrapper::vibrate(int p_duration_ms) { if (_vibrate) { JNIEnv *env = get_jni_env(); diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 1efdffd71b..ba42d5dccd 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -49,12 +49,10 @@ private: GodotJavaViewWrapper *godot_view = nullptr; - jmethodID _on_video_init = nullptr; jmethodID _restart = nullptr; jmethodID _finish = nullptr; jmethodID _set_keep_screen_on = nullptr; jmethodID _alert = nullptr; - jmethodID _get_GLES_version_code = nullptr; jmethodID _get_clipboard = nullptr; jmethodID _set_clipboard = nullptr; jmethodID _has_clipboard = nullptr; @@ -63,13 +61,10 @@ private: jmethodID _get_granted_permissions = nullptr; jmethodID _get_ca_certificates = nullptr; jmethodID _init_input_devices = nullptr; - jmethodID _get_surface = nullptr; - jmethodID _is_activity_resumed = nullptr; jmethodID _vibrate = nullptr; jmethodID _get_input_fallback_mapping = nullptr; jmethodID _on_godot_setup_completed = nullptr; jmethodID _on_godot_main_loop_started = nullptr; - jmethodID _get_class_loader = nullptr; jmethodID _create_new_godot_instance = nullptr; jmethodID _get_render_view = nullptr; jmethodID _begin_benchmark_measure = nullptr; @@ -81,19 +76,15 @@ public: ~GodotJavaWrapper(); jobject get_activity(); - jobject get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env = nullptr); - jobject get_class_loader(); GodotJavaViewWrapper *get_godot_view(); - bool on_video_init(JNIEnv *p_env = nullptr); void on_godot_setup_completed(JNIEnv *p_env = nullptr); void on_godot_main_loop_started(JNIEnv *p_env = nullptr); void restart(JNIEnv *p_env = nullptr); bool force_quit(JNIEnv *p_env = nullptr, int p_instance_id = 0); void set_keep_screen_on(bool p_enabled); void alert(const String &p_message, const String &p_title); - int get_gles_version_code(); bool has_get_clipboard(); String get_clipboard(); bool has_set_clipboard(); @@ -105,8 +96,6 @@ public: Vector<String> get_granted_permissions() const; String get_ca_certificates() const; void init_input_devices(); - jobject get_surface(); - bool is_activity_resumed(); void vibrate(int p_duration_ms); String get_input_fallback_mapping(); int create_new_godot_instance(List<String> args); diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml index 346cc9bf35..84bc0e1277 100644 --- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml +++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml @@ -27,6 +27,9 @@ <member name="application/export_method_release" type="int" setter="" getter=""> Application distribution target (release export). </member> + <member name="application/export_project_only" type="bool" setter="" getter=""> + If [code]true[/code], exports iOS project files without building an XCArchive or [code].ipa[/code] file. If [code]false[/code], exports iOS project files and builds an XCArchive and [code].ipa[/code] file at the same time. When combining Godot with Fastlane or other build pipelines, you may want to set this to [code]true[/code]. + </member> <member name="application/icon_interpolation" type="int" setter="" getter=""> Interpolation method used to resize application icon. </member> diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index aab46a7854..35dc058808 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -182,6 +182,8 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/launch_screens_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/export_project_only"), false)); + Vector<PluginConfigIOS> found_plugins = get_plugins(); for (int i = 0; i < found_plugins.size(); i++) { r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("plugins"), found_plugins[i].name)), false)); @@ -1489,7 +1491,9 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres String dest_dir = p_path.get_base_dir() + "/"; String binary_name = p_path.get_file().get_basename(); - EditorProgress ep("export", "Exporting for iOS", 5, true); + bool export_project_only = p_preset->get("application/export_project_only"); + + EditorProgress ep("export", export_project_only ? TTR("Exporting for iOS (Project Files Only)") : TTR("Exporting for iOS"), export_project_only ? 2 : 5, true); String team_id = p_preset->get("application/app_store_team_id"); ERR_FAIL_COND_V_MSG(team_id.length() == 0, ERR_CANT_OPEN, "App Store Team ID not specified - cannot configure the project."); @@ -1851,6 +1855,10 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres } } + if (export_project_only) { + return OK; + } + if (ep.step("Making .xcarchive", 3)) { return ERR_SKIP; } diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index de9f066675..ac0659ee7f 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -3256,14 +3256,14 @@ bool DisplayServerMacOS::window_is_focused(WindowID p_window) const { } bool DisplayServerMacOS::window_can_draw(WindowID p_window) const { - return window_get_mode(p_window) != WINDOW_MODE_MINIMIZED; + return (window_get_mode(p_window) != WINDOW_MODE_MINIMIZED) && [windows[p_window].window_object isOnActiveSpace]; } bool DisplayServerMacOS::can_any_window_draw() const { _THREAD_SAFE_METHOD_ for (const KeyValue<WindowID, WindowData> &E : windows) { - if (window_get_mode(E.key) != WINDOW_MODE_MINIMIZED) { + if ((window_get_mode(E.key) != WINDOW_MODE_MINIMIZED) && [E.value.window_object isOnActiveSpace]) { return true; } } diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 39e89c9db7..81f9707f6b 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -1361,7 +1361,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p String src_pkg_name; - EditorProgress ep("export", "Exporting for macOS", 3, true); + EditorProgress ep("export", TTR("Exporting for macOS"), 3, true); if (p_debug) { src_pkg_name = p_preset->get("custom_template/debug"); diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp index 1b27880efb..016a87e31d 100644 --- a/platform/uwp/export/export_plugin.cpp +++ b/platform/uwp/export/export_plugin.cpp @@ -266,7 +266,7 @@ Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_p String src_appx; - EditorProgress ep("export", "Exporting for UWP", 7, true); + EditorProgress ep("export", TTR("Exporting for UWP"), 7, true); if (p_debug) { src_appx = p_preset->get("custom_template/debug"); diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 86b909234a..73e0ec6511 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -32,6 +32,7 @@ #include "os_windows.h" +#include "core/config/project_settings.h" #include "core/io/marshalls.h" #include "main/main.h" #include "scene/resources/atlas_texture.h" @@ -42,6 +43,8 @@ #include <avrt.h> #include <dwmapi.h> +#include <shlwapi.h> +#include <shobjidl.h> #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 @@ -87,6 +90,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const { case FEATURE_HIDPI: case FEATURE_ICON: case FEATURE_NATIVE_ICON: + case FEATURE_NATIVE_DIALOG: case FEATURE_SWAP_BUFFERS: case FEATURE_KEEP_SCREEN_ON: case FEATURE_TEXT_TO_SPEECH: @@ -213,6 +217,129 @@ void DisplayServerWindows::tts_stop() { tts->stop(); } +Error DisplayServerWindows::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { + _THREAD_SAFE_METHOD_ + + Vector<Char16String> filter_names; + Vector<Char16String> filter_exts; + for (const String &E : p_filters) { + Vector<String> tokens = E.split(";"); + if (tokens.size() == 2) { + filter_exts.push_back(tokens[0].strip_edges().utf16()); + filter_names.push_back(tokens[1].strip_edges().utf16()); + } else if (tokens.size() == 1) { + filter_exts.push_back(tokens[0].strip_edges().utf16()); + filter_names.push_back(tokens[0].strip_edges().utf16()); + } + } + + Vector<COMDLG_FILTERSPEC> filters; + for (int i = 0; i < filter_names.size(); i++) { + filters.push_back({ (LPCWSTR)filter_names[i].ptr(), (LPCWSTR)filter_exts[i].ptr() }); + } + + HRESULT hr = S_OK; + IFileDialog *pfd = nullptr; + if (p_mode == FILE_DIALOG_MODE_SAVE_FILE) { + hr = CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileSaveDialog, (void **)&pfd); + } else { + hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, (void **)&pfd); + } + if (SUCCEEDED(hr)) { + DWORD flags; + pfd->GetOptions(&flags); + if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) { + flags |= FOS_ALLOWMULTISELECT; + } + if (p_mode == FILE_DIALOG_MODE_OPEN_DIR) { + flags |= FOS_PICKFOLDERS; + } + if (p_show_hidden) { + flags |= FOS_FORCESHOWHIDDEN; + } + pfd->SetOptions(flags | FOS_FORCEFILESYSTEM); + pfd->SetTitle((LPCWSTR)p_title.utf16().ptr()); + + String dir = ProjectSettings::get_singleton()->globalize_path(p_current_directory); + if (dir == ".") { + dir = OS::get_singleton()->get_executable_path().get_base_dir(); + } + dir = dir.replace("/", "\\"); + + IShellItem *shellitem = nullptr; + hr = SHCreateItemFromParsingName((LPCWSTR)dir.utf16().ptr(), nullptr, IID_IShellItem, (void **)&shellitem); + if (SUCCEEDED(hr)) { + pfd->SetDefaultFolder(shellitem); + pfd->SetFolder(shellitem); + } + + pfd->SetFileName((LPCWSTR)p_filename.utf16().ptr()); + pfd->SetFileTypes(filters.size(), filters.ptr()); + pfd->SetFileTypeIndex(0); + + hr = pfd->Show(nullptr); + if (SUCCEEDED(hr)) { + Vector<String> file_names; + + if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) { + IShellItemArray *results; + hr = static_cast<IFileOpenDialog *>(pfd)->GetResults(&results); + if (SUCCEEDED(hr)) { + DWORD count = 0; + results->GetCount(&count); + for (DWORD i = 0; i < count; i++) { + IShellItem *result; + results->GetItemAt(i, &result); + + PWSTR file_path = nullptr; + hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path); + if (SUCCEEDED(hr)) { + file_names.push_back(String::utf16((const char16_t *)file_path)); + CoTaskMemFree(file_path); + } + result->Release(); + } + results->Release(); + } + } else { + IShellItem *result; + hr = pfd->GetResult(&result); + if (SUCCEEDED(hr)) { + PWSTR file_path = nullptr; + hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path); + if (SUCCEEDED(hr)) { + file_names.push_back(String::utf16((const char16_t *)file_path)); + CoTaskMemFree(file_path); + } + result->Release(); + } + } + if (!p_callback.is_null()) { + Variant v_status = true; + Variant v_files = file_names; + Variant *v_args[2] = { &v_status, &v_files }; + Variant ret; + Callable::CallError ce; + p_callback.callp((const Variant **)&v_args, 2, ret, ce); + } + } else { + if (!p_callback.is_null()) { + Variant v_status = false; + Variant v_files = Vector<String>(); + Variant *v_args[2] = { &v_status, &v_files }; + Variant ret; + Callable::CallError ce; + p_callback.callp((const Variant **)&v_args, 2, ret, ce); + } + } + pfd->Release(); + + return OK; + } else { + return ERR_CANT_OPEN; + } +} + void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) { _THREAD_SAFE_METHOD_ diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 9d1088675b..bd47dee9ec 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -511,6 +511,8 @@ public: virtual bool is_dark_mode() const override; virtual Color get_accent_color() const override; + virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override; + virtual void mouse_set_mode(MouseMode p_mode) override; virtual MouseMode mouse_get_mode() const override; diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index 10fc7ef5b2..70ec57aa7a 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -142,7 +142,7 @@ void CollisionShape2D::set_shape(const Ref<Shape2D> &p_shape) { return; } if (shape.is_valid()) { - shape->disconnect("changed", callable_mp(this, &CollisionShape2D::_shape_changed)); + shape->disconnect_changed(callable_mp(this, &CollisionShape2D::_shape_changed)); } shape = p_shape; queue_redraw(); @@ -155,7 +155,7 @@ void CollisionShape2D::set_shape(const Ref<Shape2D> &p_shape) { } if (shape.is_valid()) { - shape->connect("changed", callable_mp(this, &CollisionShape2D::_shape_changed)); + shape->connect_changed(callable_mp(this, &CollisionShape2D::_shape_changed)); } update_configuration_warnings(); diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 899c94769a..1c193f991e 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -30,7 +30,6 @@ #include "cpu_particles_2d.h" -#include "core/core_string_names.h" #include "scene/2d/gpu_particles_2d.h" #include "scene/resources/atlas_texture.h" #include "scene/resources/curve_texture.h" @@ -207,13 +206,13 @@ void CPUParticles2D::set_texture(const Ref<Texture2D> &p_texture) { } if (texture.is_valid()) { - texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CPUParticles2D::_texture_changed)); + texture->disconnect_changed(callable_mp(this, &CPUParticles2D::_texture_changed)); } texture = p_texture; if (texture.is_valid()) { - texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CPUParticles2D::_texture_changed)); + texture->connect_changed(callable_mp(this, &CPUParticles2D::_texture_changed)); } queue_redraw(); diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 52455e1f9e..8c5782dc41 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -30,7 +30,6 @@ #include "gpu_particles_2d.h" -#include "core/core_string_names.h" #include "scene/resources/atlas_texture.h" #include "scene/resources/particle_process_material.h" #include "scene/scene_string_names.h" @@ -357,13 +356,13 @@ Rect2 GPUParticles2D::capture_rect() const { void GPUParticles2D::set_texture(const Ref<Texture2D> &p_texture) { if (texture.is_valid()) { - texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GPUParticles2D::_texture_changed)); + texture->disconnect_changed(callable_mp(this, &GPUParticles2D::_texture_changed)); } texture = p_texture; if (texture.is_valid()) { - texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GPUParticles2D::_texture_changed)); + texture->connect_changed(callable_mp(this, &GPUParticles2D::_texture_changed)); } _update_collision_size(); queue_redraw(); diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index 4c3161d049..61f5d5cd46 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -215,7 +215,7 @@ bool LightOccluder2D::_edit_is_selected_on_click(const Point2 &p_point, double p void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon) { #ifdef DEBUG_ENABLED if (occluder_polygon.is_valid()) { - occluder_polygon->disconnect("changed", callable_mp(this, &LightOccluder2D::_poly_changed)); + occluder_polygon->disconnect_changed(callable_mp(this, &LightOccluder2D::_poly_changed)); } #endif occluder_polygon = p_polygon; @@ -228,7 +228,7 @@ void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polyg #ifdef DEBUG_ENABLED if (occluder_polygon.is_valid()) { - occluder_polygon->connect("changed", callable_mp(this, &LightOccluder2D::_poly_changed)); + occluder_polygon->connect_changed(callable_mp(this, &LightOccluder2D::_poly_changed)); } queue_redraw(); #endif diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index 58dad40403..3a9473d76c 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -30,7 +30,6 @@ #include "line_2d.h" -#include "core/core_string_names.h" #include "core/math/geometry_2d.h" #include "line_builder.h" @@ -89,14 +88,14 @@ float Line2D::get_width() const { void Line2D::set_curve(const Ref<Curve> &p_curve) { // Cleanup previous connection if any if (_curve.is_valid()) { - _curve->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Line2D::_curve_changed)); + _curve->disconnect_changed(callable_mp(this, &Line2D::_curve_changed)); } _curve = p_curve; // Connect to the curve so the line will update when it is changed if (_curve.is_valid()) { - _curve->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Line2D::_curve_changed)); + _curve->connect_changed(callable_mp(this, &Line2D::_curve_changed)); } queue_redraw(); @@ -159,14 +158,14 @@ Color Line2D::get_default_color() const { void Line2D::set_gradient(const Ref<Gradient> &p_gradient) { // Cleanup previous connection if any if (_gradient.is_valid()) { - _gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Line2D::_gradient_changed)); + _gradient->disconnect_changed(callable_mp(this, &Line2D::_gradient_changed)); } _gradient = p_gradient; // Connect to the gradient so the line will update when the Gradient is changed if (_gradient.is_valid()) { - _gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Line2D::_gradient_changed)); + _gradient->connect_changed(callable_mp(this, &Line2D::_gradient_changed)); } queue_redraw(); diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp index f347eb6520..9631b2cc4e 100644 --- a/scene/2d/multimesh_instance_2d.cpp +++ b/scene/2d/multimesh_instance_2d.cpp @@ -30,7 +30,6 @@ #include "multimesh_instance_2d.h" -#include "core/core_string_names.h" #include "scene/scene_string_names.h" void MultiMeshInstance2D::_notification(int p_what) { @@ -59,13 +58,13 @@ void MultiMeshInstance2D::_bind_methods() { void MultiMeshInstance2D::set_multimesh(const Ref<MultiMesh> &p_multimesh) { // Cleanup previous connection if any. if (multimesh.is_valid()) { - multimesh->disconnect(CoreStringNames::get_singleton()->changed, callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); + multimesh->disconnect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); } multimesh = p_multimesh; // Connect to the multimesh so the AABB can update when instance transforms are changed. if (multimesh.is_valid()) { - multimesh->connect(CoreStringNames::get_singleton()->changed, callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); + multimesh->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); } queue_redraw(); } diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 529545de97..fa03713592 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -30,7 +30,6 @@ #include "navigation_region_2d.h" -#include "core/core_string_names.h" #include "core/math/geometry_2d.h" #include "scene/2d/navigation_obstacle_2d.h" #include "scene/resources/world_2d.h" @@ -193,14 +192,14 @@ void NavigationRegion2D::set_navigation_polygon(const Ref<NavigationPolygon> &p_ } if (navigation_polygon.is_valid()) { - navigation_polygon->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed)); + navigation_polygon->disconnect_changed(callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed)); } navigation_polygon = p_navigation_polygon; NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, p_navigation_polygon); if (navigation_polygon.is_valid()) { - navigation_polygon->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed)); + navigation_polygon->connect_changed(callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed)); } _navigation_polygon_changed(); diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 3e6a484e72..ee0ccc42ff 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -182,13 +182,13 @@ void Path2D::_curve_changed() { void Path2D::set_curve(const Ref<Curve2D> &p_curve) { if (curve.is_valid()) { - curve->disconnect("changed", callable_mp(this, &Path2D::_curve_changed)); + curve->disconnect_changed(callable_mp(this, &Path2D::_curve_changed)); } curve = p_curve; if (curve.is_valid()) { - curve->connect("changed", callable_mp(this, &Path2D::_curve_changed)); + curve->connect_changed(callable_mp(this, &Path2D::_curve_changed)); } _curve_changed(); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index b3acc1849b..b9bde47507 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -30,7 +30,6 @@ #include "physics_body_2d.h" -#include "core/core_string_names.h" #include "scene/scene_string_names.h" void PhysicsBody2D::_bind_methods() { @@ -195,15 +194,13 @@ real_t StaticBody2D::get_constant_angular_velocity() const { void StaticBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { if (physics_material_override.is_valid()) { - if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &StaticBody2D::_reload_physics_characteristics))) { - physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &StaticBody2D::_reload_physics_characteristics)); - } + physics_material_override->disconnect_changed(callable_mp(this, &StaticBody2D::_reload_physics_characteristics)); } physics_material_override = p_physics_material_override; if (physics_material_override.is_valid()) { - physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &StaticBody2D::_reload_physics_characteristics)); + physics_material_override->connect_changed(callable_mp(this, &StaticBody2D::_reload_physics_characteristics)); } _reload_physics_characteristics(); } @@ -651,15 +648,13 @@ const Vector2 &RigidBody2D::get_center_of_mass() const { void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { if (physics_material_override.is_valid()) { - if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics))) { - physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics)); - } + physics_material_override->disconnect_changed(callable_mp(this, &RigidBody2D::_reload_physics_characteristics)); } physics_material_override = p_physics_material_override; if (physics_material_override.is_valid()) { - physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics)); + physics_material_override->connect_changed(callable_mp(this, &RigidBody2D::_reload_physics_characteristics)); } _reload_physics_characteristics(); } diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp index f1a9119458..90d80d7549 100644 --- a/scene/2d/shape_cast_2d.cpp +++ b/scene/2d/shape_cast_2d.cpp @@ -31,7 +31,6 @@ #include "shape_cast_2d.h" #include "core/config/engine.h" -#include "core/core_string_names.h" #include "scene/2d/collision_object_2d.h" #include "scene/2d/physics_body_2d.h" #include "scene/resources/circle_shape_2d.h" @@ -155,11 +154,11 @@ void ShapeCast2D::set_shape(const Ref<Shape2D> &p_shape) { return; } if (shape.is_valid()) { - shape->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast2D::_shape_changed)); + shape->disconnect_changed(callable_mp(this, &ShapeCast2D::_shape_changed)); } shape = p_shape; if (shape.is_valid()) { - shape->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast2D::_shape_changed)); + shape->connect_changed(callable_mp(this, &ShapeCast2D::_shape_changed)); shape_rid = shape->get_rid(); } diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp index 6e314160fd..7e6b43559c 100644 --- a/scene/2d/sprite_2d.cpp +++ b/scene/2d/sprite_2d.cpp @@ -30,7 +30,6 @@ #include "sprite_2d.h" -#include "core/core_string_names.h" #include "scene/main/window.h" #include "scene/scene_string_names.h" @@ -137,13 +136,13 @@ void Sprite2D::set_texture(const Ref<Texture2D> &p_texture) { } if (texture.is_valid()) { - texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Sprite2D::_texture_changed)); + texture->disconnect_changed(callable_mp(this, &Sprite2D::_texture_changed)); } texture = p_texture; if (texture.is_valid()) { - texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Sprite2D::_texture_changed)); + texture->connect_changed(callable_mp(this, &Sprite2D::_texture_changed)); } queue_redraw(); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index c5ce45091e..8f41bb6146 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -524,7 +524,7 @@ void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { // Set the tileset, registering to its changes. if (tile_set.is_valid()) { - tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed)); + tile_set->disconnect_changed(callable_mp(this, &TileMap::_tile_set_changed)); } if (!p_tileset.is_valid()) { @@ -534,7 +534,7 @@ void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { tile_set = p_tileset; if (tile_set.is_valid()) { - tile_set->connect("changed", callable_mp(this, &TileMap::_tile_set_changed)); + tile_set->connect_changed(callable_mp(this, &TileMap::_tile_set_changed)); _clear_internals(); _recreate_internals(); } @@ -4250,7 +4250,7 @@ TileMap::TileMap() { TileMap::~TileMap() { if (tile_set.is_valid()) { - tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed)); + tile_set->disconnect_changed(callable_mp(this, &TileMap::_tile_set_changed)); } _clear_internals(); diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp index 10a9e7edec..5ed7fadb2a 100644 --- a/scene/2d/touch_screen_button.cpp +++ b/scene/2d/touch_screen_button.cpp @@ -82,11 +82,11 @@ void TouchScreenButton::set_shape(const Ref<Shape2D> &p_shape) { return; } if (shape.is_valid()) { - shape->disconnect("changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); + shape->disconnect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); } shape = p_shape; if (shape.is_valid()) { - shape->connect("changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); + shape->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); } queue_redraw(); } diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 225b9b35b3..37ceb9d1a1 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -31,7 +31,6 @@ #include "camera_3d.h" #include "collision_object_3d.h" -#include "core/core_string_names.h" #include "core/math/projection.h" #include "scene/main/viewport.h" @@ -430,7 +429,7 @@ void Camera3D::set_attributes(const Ref<CameraAttributes> &p_attributes) { if (attributes.is_valid()) { CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); if (physical_attributes) { - attributes->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Camera3D::_attributes_changed)); + attributes->disconnect_changed(callable_mp(this, &Camera3D::_attributes_changed)); } } @@ -439,7 +438,7 @@ void Camera3D::set_attributes(const Ref<CameraAttributes> &p_attributes) { if (attributes.is_valid()) { CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); if (physical_attributes) { - attributes->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Camera3D::_attributes_changed)); + attributes->connect_changed(callable_mp(this, &Camera3D::_attributes_changed)); _attributes_changed(); } diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 6d8d60dcaa..bfe594adc2 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -394,11 +394,7 @@ void CollisionObject3D::_update_debug_shapes() { if (s.debug_shape.is_null()) { s.debug_shape = RS::get_singleton()->instance_create(); RS::get_singleton()->instance_set_scenario(s.debug_shape, get_world_3d()->get_scenario()); - - if (!s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_shape_changed))) { - s.shape->connect("changed", callable_mp(this, &CollisionObject3D::_shape_changed).bind(s.shape), CONNECT_DEFERRED); - } - + s.shape->connect_changed(callable_mp(this, &CollisionObject3D::_shape_changed).bind(s.shape), CONNECT_DEFERRED); ++debug_shapes_count; } @@ -422,8 +418,8 @@ void CollisionObject3D::_clear_debug_shapes() { if (s.debug_shape.is_valid()) { RS::get_singleton()->free(s.debug_shape); s.debug_shape = RID(); - if (s.shape.is_valid() && s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_update_shape_data))) { - s.shape->disconnect("changed", callable_mp(this, &CollisionObject3D::_update_shape_data)); + if (s.shape.is_valid()) { + s.shape->disconnect_changed(callable_mp(this, &CollisionObject3D::_update_shape_data)); } } } @@ -663,8 +659,8 @@ void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) if (s.debug_shape.is_valid()) { RS::get_singleton()->free(s.debug_shape); - if (s.shape.is_valid() && s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_shape_changed))) { - s.shape->disconnect("changed", callable_mp(this, &CollisionObject3D::_shape_changed)); + if (s.shape.is_valid()) { + s.shape->disconnect_changed(callable_mp(this, &CollisionObject3D::_shape_changed)); } --debug_shapes_count; } diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index 10eefc784d..0bb0382301 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -112,9 +112,10 @@ void CollisionShape3D::_notification(int p_what) { } } +#ifndef DISABLE_DEPRECATED void CollisionShape3D::resource_changed(Ref<Resource> res) { - update_gizmos(); } +#endif PackedStringArray CollisionShape3D::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); @@ -145,8 +146,9 @@ PackedStringArray CollisionShape3D::get_configuration_warnings() const { } void CollisionShape3D::_bind_methods() { - //not sure if this should do anything +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &CollisionShape3D::resource_changed); +#endif ClassDB::bind_method(D_METHOD("set_shape", "shape"), &CollisionShape3D::set_shape); ClassDB::bind_method(D_METHOD("get_shape"), &CollisionShape3D::get_shape); ClassDB::bind_method(D_METHOD("set_disabled", "enable"), &CollisionShape3D::set_disabled); @@ -162,12 +164,12 @@ void CollisionShape3D::set_shape(const Ref<Shape3D> &p_shape) { if (p_shape == shape) { return; } - if (!shape.is_null()) { - shape->unregister_owner(this); + if (shape.is_valid()) { + shape->disconnect_changed(callable_mp((Node3D *)this, &Node3D::update_gizmos)); } shape = p_shape; - if (!shape.is_null()) { - shape->register_owner(this); + if (shape.is_valid()) { + shape->connect_changed(callable_mp((Node3D *)this, &Node3D::update_gizmos)); } update_gizmos(); if (collision_object) { @@ -206,8 +208,5 @@ CollisionShape3D::CollisionShape3D() { } CollisionShape3D::~CollisionShape3D() { - if (!shape.is_null()) { - shape->unregister_owner(this); - } //RenderingServer::get_singleton()->free(indicator); } diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h index 74928bad6d..bc0e70f8ac 100644 --- a/scene/3d/collision_shape_3d.h +++ b/scene/3d/collision_shape_3d.h @@ -43,7 +43,9 @@ class CollisionShape3D : public Node3D { uint32_t owner_id = 0; CollisionObject3D *collision_object = nullptr; +#ifndef DISABLE_DEPRECATED void resource_changed(Ref<Resource> res); +#endif bool disabled = false; protected: diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index fb889c928d..3a23cbcff1 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -234,13 +234,13 @@ void GPUParticles3D::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) { ERR_FAIL_INDEX(p_pass, draw_passes.size()); if (Engine::get_singleton()->is_editor_hint() && draw_passes.write[p_pass].is_valid()) { - draw_passes.write[p_pass]->disconnect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings)); + draw_passes.write[p_pass]->disconnect_changed(callable_mp((Node *)this, &Node::update_configuration_warnings)); } draw_passes.write[p_pass] = p_mesh; if (Engine::get_singleton()->is_editor_hint() && draw_passes.write[p_pass].is_valid()) { - draw_passes.write[p_pass]->connect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings), CONNECT_DEFERRED); + draw_passes.write[p_pass]->connect_changed(callable_mp((Node *)this, &Node::update_configuration_warnings), CONNECT_DEFERRED); } RID mesh_rid; diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 296218bf25..b0e7c73253 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -30,7 +30,6 @@ #include "label_3d.h" -#include "core/core_string_names.h" #include "scene/main/viewport.h" #include "scene/resources/theme.h" #include "scene/scene_string_names.h" @@ -762,12 +761,12 @@ void Label3D::_font_changed() { void Label3D::set_font(const Ref<Font> &p_font) { if (font_override != p_font) { if (font_override.is_valid()) { - font_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label3D::_font_changed)); + font_override->disconnect_changed(callable_mp(this, &Label3D::_font_changed)); } font_override = p_font; dirty_font = true; if (font_override.is_valid()) { - font_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label3D::_font_changed)); + font_override->connect_changed(callable_mp(this, &Label3D::_font_changed)); } _queue_update(); } @@ -779,7 +778,7 @@ Ref<Font> Label3D::get_font() const { Ref<Font> Label3D::_get_font_or_default() const { if (theme_font.is_valid()) { - theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed)); + theme_font->disconnect_changed(callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed)); theme_font.unref(); } @@ -797,7 +796,7 @@ Ref<Font> Label3D::_get_font_or_default() const { Ref<Font> f = ThemeDB::get_singleton()->get_project_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); if (f.is_valid()) { theme_font = f; - theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed)); + theme_font->connect_changed(callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed)); } return f; } @@ -814,7 +813,7 @@ Ref<Font> Label3D::_get_font_or_default() const { Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); if (f.is_valid()) { theme_font = f; - theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed)); + theme_font->connect_changed(callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed)); } return f; } @@ -825,7 +824,7 @@ Ref<Font> Label3D::_get_font_or_default() const { Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); if (f.is_valid()) { theme_font = f; - theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed)); + theme_font->connect_changed(callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed)); } return f; } diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 28a3cd0b13..0b0b098f65 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -31,7 +31,6 @@ #include "mesh_instance_3d.h" #include "collision_shape_3d.h" -#include "core/core_string_names.h" #include "physics_body_3d.h" #include "scene/resources/concave_polygon_shape_3d.h" #include "scene/resources/convex_polygon_shape_3d.h" @@ -111,7 +110,7 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) { } if (mesh.is_valid()) { - mesh->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed)); + mesh->disconnect_changed(callable_mp(this, &MeshInstance3D::_mesh_changed)); } mesh = p_mesh; @@ -120,7 +119,7 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) { // If mesh is a PrimitiveMesh, calling get_rid on it can trigger a changed callback // so do this before connecting _mesh_changed. set_base(mesh->get_rid()); - mesh->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed)); + mesh->connect_changed(callable_mp(this, &MeshInstance3D::_mesh_changed)); _mesh_changed(); } else { blend_shape_tracks.clear(); diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 194d3082df..3f1cfae99d 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -30,7 +30,6 @@ #include "navigation_region_3d.h" -#include "core/core_string_names.h" #include "scene/resources/navigation_mesh_source_geometry_data_3d.h" #include "servers/navigation_server_3d.h" @@ -193,13 +192,13 @@ void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_naviga } if (navigation_mesh.is_valid()) { - navigation_mesh->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed)); + navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed)); } navigation_mesh = p_navigation_mesh; if (navigation_mesh.is_valid()) { - navigation_mesh->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed)); + navigation_mesh->connect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed)); } NavigationServer3D::get_singleton()->region_set_navigation_mesh(region, p_navigation_mesh); @@ -262,11 +261,19 @@ void _bake_navigation_mesh(void *p_user_data) { Ref<NavigationMeshSourceGeometryData3D> source_geometry_data = args->source_geometry_data; NavigationServer3D::get_singleton()->bake_from_source_geometry_data(nav_mesh, source_geometry_data); - args->nav_region->call_deferred(SNAME("_bake_finished"), nav_mesh); + if (!Thread::is_main_thread()) { + args->nav_region->call_deferred(SNAME("_bake_finished"), nav_mesh); + } else { + args->nav_region->_bake_finished(nav_mesh); + } memdelete(args); } else { ERR_PRINT("Can't bake the navigation mesh if the `NavigationMesh` resource doesn't exist"); - args->nav_region->call_deferred(SNAME("_bake_finished"), Ref<NavigationMesh>()); + if (!Thread::is_main_thread()) { + args->nav_region->call_deferred(SNAME("_bake_finished"), Ref<NavigationMesh>()); + } else { + args->nav_region->_bake_finished(Ref<NavigationMesh>()); + } memdelete(args); } } @@ -462,7 +469,7 @@ NavigationRegion3D::~NavigationRegion3D() { } if (navigation_mesh.is_valid()) { - navigation_mesh->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed)); + navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed)); } ERR_FAIL_NULL(NavigationServer3D::get_singleton()); NavigationServer3D::get_singleton()->free(region); diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index 8fd1df372b..7b535f6169 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -31,7 +31,6 @@ #include "occluder_instance_3d.h" #include "core/config/project_settings.h" -#include "core/core_string_names.h" #include "core/io/marshalls.h" #include "core/math/geometry_2d.h" #include "core/math/triangulate.h" @@ -441,14 +440,14 @@ void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &p_occluder) { } if (occluder.is_valid()) { - occluder->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &OccluderInstance3D::_occluder_changed)); + occluder->disconnect_changed(callable_mp(this, &OccluderInstance3D::_occluder_changed)); } occluder = p_occluder; if (occluder.is_valid()) { set_base(occluder->get_rid()); - occluder->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &OccluderInstance3D::_occluder_changed)); + occluder->connect_changed(callable_mp(this, &OccluderInstance3D::_occluder_changed)); } else { set_base(RID()); } diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 9516973ae2..6aea063096 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -144,13 +144,13 @@ void Path3D::_curve_changed() { void Path3D::set_curve(const Ref<Curve3D> &p_curve) { if (curve.is_valid()) { - curve->disconnect("changed", callable_mp(this, &Path3D::_curve_changed)); + curve->disconnect_changed(callable_mp(this, &Path3D::_curve_changed)); } curve = p_curve; if (curve.is_valid()) { - curve->connect("changed", callable_mp(this, &Path3D::_curve_changed)); + curve->connect_changed(callable_mp(this, &Path3D::_curve_changed)); } _curve_changed(); } diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 6b31aa0a3c..e49b43c650 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -30,7 +30,6 @@ #include "physics_body_3d.h" -#include "core/core_string_names.h" #include "scene/scene_string_names.h" void PhysicsBody3D::_bind_methods() { @@ -214,15 +213,13 @@ real_t PhysicsBody3D::get_inverse_mass() const { void StaticBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { if (physics_material_override.is_valid()) { - if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &StaticBody3D::_reload_physics_characteristics))) { - physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &StaticBody3D::_reload_physics_characteristics)); - } + physics_material_override->disconnect_changed(callable_mp(this, &StaticBody3D::_reload_physics_characteristics)); } physics_material_override = p_physics_material_override; if (physics_material_override.is_valid()) { - physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &StaticBody3D::_reload_physics_characteristics)); + physics_material_override->connect_changed(callable_mp(this, &StaticBody3D::_reload_physics_characteristics)); } _reload_physics_characteristics(); } @@ -726,15 +723,13 @@ const Vector3 &RigidBody3D::get_center_of_mass() const { void RigidBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { if (physics_material_override.is_valid()) { - if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics))) { - physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics)); - } + physics_material_override->disconnect_changed(callable_mp(this, &RigidBody3D::_reload_physics_characteristics)); } physics_material_override = p_physics_material_override; if (physics_material_override.is_valid()) { - physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics)); + physics_material_override->connect_changed(callable_mp(this, &RigidBody3D::_reload_physics_characteristics)); } _reload_physics_characteristics(); } diff --git a/scene/3d/shape_cast_3d.cpp b/scene/3d/shape_cast_3d.cpp index 75f94b36d3..b6401832ed 100644 --- a/scene/3d/shape_cast_3d.cpp +++ b/scene/3d/shape_cast_3d.cpp @@ -30,7 +30,6 @@ #include "shape_cast_3d.h" -#include "core/core_string_names.h" #include "scene/3d/collision_object_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/resources/concave_polygon_shape_3d.h" @@ -93,7 +92,9 @@ void ShapeCast3D::_notification(int p_what) { } void ShapeCast3D::_bind_methods() { +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &ShapeCast3D::resource_changed); +#endif ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &ShapeCast3D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &ShapeCast3D::is_enabled); @@ -312,12 +313,10 @@ real_t ShapeCast3D::get_closest_collision_unsafe_fraction() const { return collision_unsafe_fraction; } +#ifndef DISABLE_DEPRECATED void ShapeCast3D::resource_changed(Ref<Resource> p_res) { - if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) { - _update_debug_shape(); - } - update_gizmos(); } +#endif void ShapeCast3D::_shape_changed() { update_gizmos(); @@ -332,13 +331,11 @@ void ShapeCast3D::set_shape(const Ref<Shape3D> &p_shape) { return; } if (shape.is_valid()) { - shape->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast3D::_shape_changed)); - shape->unregister_owner(this); + shape->disconnect_changed(callable_mp(this, &ShapeCast3D::_shape_changed)); } shape = p_shape; if (shape.is_valid()) { - shape->register_owner(this); - shape->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast3D::_shape_changed)); + shape->connect_changed(callable_mp(this, &ShapeCast3D::_shape_changed)); shape_rid = shape->get_rid(); } @@ -633,9 +630,3 @@ void ShapeCast3D::_clear_debug_shape() { debug_shape = nullptr; } - -ShapeCast3D::~ShapeCast3D() { - if (!shape.is_null()) { - shape->unregister_owner(this); - } -} diff --git a/scene/3d/shape_cast_3d.h b/scene/3d/shape_cast_3d.h index 98158d3c7c..043e35090f 100644 --- a/scene/3d/shape_cast_3d.h +++ b/scene/3d/shape_cast_3d.h @@ -40,7 +40,9 @@ class ShapeCast3D : public Node3D { GDCLASS(ShapeCast3D, Node3D); bool enabled = true; +#ifndef DISABLE_DEPRECATED void resource_changed(Ref<Resource> p_res); +#endif Ref<Shape3D> shape; RID shape_rid; @@ -74,8 +76,6 @@ class ShapeCast3D : public Node3D { Array _get_collision_result() const; - ~ShapeCast3D(); - protected: void _notification(int p_what); void _update_shapecast_state(); diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 51359059ef..445c1003b5 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -887,7 +887,7 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { skin_bindings.insert(skin_ref.operator->()); - skin_ref->skin->connect("changed", callable_mp(skin_ref.operator->(), &SkinReference::_skin_changed)); + skin_ref->skin->connect_changed(callable_mp(skin_ref.operator->(), &SkinReference::_skin_changed)); _make_dirty(); // Skin needs to be updated, so update skeleton. diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 6a76b5ac16..412017bc5a 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -1142,7 +1142,7 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod p_node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed), CONNECT_REFERENCE_COUNTED); p_node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed), CONNECT_REFERENCE_COUNTED); p_node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed), CONNECT_REFERENCE_COUNTED); - p_node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_name), CONNECT_REFERENCE_COUNTED); + p_node->connect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_name), CONNECT_REFERENCE_COUNTED); } Ref<AnimationNode> AnimationNodeBlendTree::get_node(const StringName &p_name) const { @@ -1206,7 +1206,7 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) { node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed)); node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed)); node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed)); - node->disconnect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed)); + node->disconnect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed)); } nodes.erase(p_name); @@ -1231,7 +1231,7 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); ERR_FAIL_COND(p_new_name == SceneStringNames::get_singleton()->output); - nodes[p_name].node->disconnect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed)); + nodes[p_name].node->disconnect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed)); nodes[p_new_name] = nodes[p_name]; nodes.erase(p_name); @@ -1245,7 +1245,7 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN } } // Connection must be done with new name. - nodes[p_new_name].node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_new_name), CONNECT_REFERENCE_COUNTED); + nodes[p_new_name].node->connect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_new_name), CONNECT_REFERENCE_COUNTED); emit_signal(SNAME("animation_node_renamed"), get_instance_id(), p_name, p_new_name); emit_signal(SNAME("tree_changed")); diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index dc3cdf04c9..430569432a 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -30,7 +30,6 @@ #include "button.h" -#include "core/core_string_names.h" #include "core/string/translation.h" #include "servers/rendering_server.h" @@ -540,13 +539,13 @@ void Button::set_icon(const Ref<Texture2D> &p_icon) { } if (icon.is_valid()) { - icon->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Button::_texture_changed)); + icon->disconnect_changed(callable_mp(this, &Button::_texture_changed)); } icon = p_icon; if (icon.is_valid()) { - icon->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Button::_texture_changed)); + icon->connect_changed(callable_mp(this, &Button::_texture_changed)); } queue_redraw(); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index d23bc8ddaa..e50a1c0e88 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -37,6 +37,8 @@ #include "core/os/os.h" #include "scene/gui/color_mode.h" #include "scene/resources/image_texture.h" +#include "scene/resources/style_box_flat.h" +#include "scene/resources/style_box_texture.h" #include "servers/display_server.h" #include "thirdparty/misc/ok_color.h" #include "thirdparty/misc/ok_color_shader.h" diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 03e47cb5c3..1a4b1b6ee1 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -46,6 +46,7 @@ #include "scene/gui/slider.h" #include "scene/gui/spin_box.h" #include "scene/gui/texture_rect.h" +#include "scene/resources/style_box_flat.h" class ColorMode; class ColorModeRGB; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 78862364d5..0ca04faf9b 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -271,21 +271,21 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) { if (name.begins_with("theme_override_icons/")) { String dname = name.get_slicec('/', 1); if (data.theme_icon_override.has(dname)) { - data.theme_icon_override[dname]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + data.theme_icon_override[dname]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed)); } data.theme_icon_override.erase(dname); _notify_theme_override_changed(); } else if (name.begins_with("theme_override_styles/")) { String dname = name.get_slicec('/', 1); if (data.theme_style_override.has(dname)) { - data.theme_style_override[dname]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + data.theme_style_override[dname]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed)); } data.theme_style_override.erase(dname); _notify_theme_override_changed(); } else if (name.begins_with("theme_override_fonts/")) { String dname = name.get_slicec('/', 1); if (data.theme_font_override.has(dname)) { - data.theme_font_override[dname]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + data.theme_font_override[dname]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed)); } data.theme_font_override.erase(dname); _notify_theme_override_changed(); @@ -2468,13 +2468,13 @@ void Control::set_theme(const Ref<Theme> &p_theme) { } if (data.theme.is_valid()) { - data.theme->disconnect("changed", callable_mp(this, &Control::_theme_changed)); + data.theme->disconnect_changed(callable_mp(this, &Control::_theme_changed)); } data.theme = p_theme; if (data.theme.is_valid()) { data.theme_owner->propagate_theme_changed(this, this, is_inside_tree(), true); - data.theme->connect("changed", callable_mp(this, &Control::_theme_changed), CONNECT_DEFERRED); + data.theme->connect_changed(callable_mp(this, &Control::_theme_changed), CONNECT_DEFERRED); return; } @@ -2769,11 +2769,11 @@ void Control::add_theme_icon_override(const StringName &p_name, const Ref<Textur ERR_FAIL_COND(!p_icon.is_valid()); if (data.theme_icon_override.has(p_name)) { - data.theme_icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + data.theme_icon_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed)); } data.theme_icon_override[p_name] = p_icon; - data.theme_icon_override[p_name]->connect("changed", callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); + data.theme_icon_override[p_name]->connect_changed(callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); _notify_theme_override_changed(); } @@ -2782,11 +2782,11 @@ void Control::add_theme_style_override(const StringName &p_name, const Ref<Style ERR_FAIL_COND(!p_style.is_valid()); if (data.theme_style_override.has(p_name)) { - data.theme_style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + data.theme_style_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed)); } data.theme_style_override[p_name] = p_style; - data.theme_style_override[p_name]->connect("changed", callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); + data.theme_style_override[p_name]->connect_changed(callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); _notify_theme_override_changed(); } @@ -2795,11 +2795,11 @@ void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> ERR_FAIL_COND(!p_font.is_valid()); if (data.theme_font_override.has(p_name)) { - data.theme_font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + data.theme_font_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed)); } data.theme_font_override[p_name] = p_font; - data.theme_font_override[p_name]->connect("changed", callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); + data.theme_font_override[p_name]->connect_changed(callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); _notify_theme_override_changed(); } @@ -2824,7 +2824,7 @@ void Control::add_theme_constant_override(const StringName &p_name, int p_consta void Control::remove_theme_icon_override(const StringName &p_name) { ERR_MAIN_THREAD_GUARD; if (data.theme_icon_override.has(p_name)) { - data.theme_icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + data.theme_icon_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed)); } data.theme_icon_override.erase(p_name); @@ -2834,7 +2834,7 @@ void Control::remove_theme_icon_override(const StringName &p_name) { void Control::remove_theme_style_override(const StringName &p_name) { ERR_MAIN_THREAD_GUARD; if (data.theme_style_override.has(p_name)) { - data.theme_style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + data.theme_style_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed)); } data.theme_style_override.erase(p_name); @@ -2844,7 +2844,7 @@ void Control::remove_theme_style_override(const StringName &p_name) { void Control::remove_theme_font_override(const StringName &p_name) { ERR_MAIN_THREAD_GUARD; if (data.theme_font_override.has(p_name)) { - data.theme_font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + data.theme_font_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed)); } data.theme_font_override.erase(p_name); @@ -3630,13 +3630,13 @@ Control::~Control() { // Resources need to be disconnected. for (KeyValue<StringName, Ref<Texture2D>> &E : data.theme_icon_override) { - E.value->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + E.value->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed)); } for (KeyValue<StringName, Ref<StyleBox>> &E : data.theme_style_override) { - E.value->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + E.value->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed)); } for (KeyValue<StringName, Ref<Font>> &E : data.theme_font_override) { - E.value->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + E.value->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed)); } // Then override maps can be simply cleared. diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index fe32e406e8..d4da4797eb 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -67,14 +67,14 @@ void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files) { if (p_files.size() > 0) { String f = p_files[0]; if (mode == FILE_MODE_OPEN_FILES) { - emit_signal("files_selected", p_files); + emit_signal(SNAME("files_selected"), p_files); } else { if (mode == FILE_MODE_SAVE_FILE) { - emit_signal("file_selected", f); + emit_signal(SNAME("file_selected"), f); } else if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) { - emit_signal("file_selected", f); + emit_signal(SNAME("file_selected"), f); } else if (mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_DIR) { - emit_signal("dir_selected", f); + emit_signal(SNAME("dir_selected"), f); } } file->set_text(f); @@ -82,7 +82,7 @@ void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files) { } } else { file->set_text(""); - emit_signal("cancelled"); + emit_signal(SNAME("canceled")); } } diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 2c37017fa1..0c9ed37046 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -36,6 +36,7 @@ #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/view_panner.h" +#include "scene/resources/style_box_flat.h" constexpr int MINIMAP_OFFSET = 12; constexpr int MINIMAP_PADDING = 5; diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 1b48b9165d..cbaf1c372e 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -31,7 +31,6 @@ #include "label.h" #include "core/config/project_settings.h" -#include "core/core_string_names.h" #include "core/string/print_string.h" #include "core/string/translation.h" @@ -784,11 +783,11 @@ void Label::_invalidate() { void Label::set_label_settings(const Ref<LabelSettings> &p_settings) { if (settings != p_settings) { if (settings.is_valid()) { - settings->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label::_invalidate)); + settings->disconnect_changed(callable_mp(this, &Label::_invalidate)); } settings = p_settings; if (settings.is_valid()) { - settings->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label::_invalidate), CONNECT_REFERENCE_COUNTED); + settings->connect_changed(callable_mp(this, &Label::_invalidate), CONNECT_REFERENCE_COUNTED); } _invalidate(); } diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp index 68e2db7cfd..e2ae824e60 100644 --- a/scene/gui/nine_patch_rect.cpp +++ b/scene/gui/nine_patch_rect.cpp @@ -30,7 +30,6 @@ #include "nine_patch_rect.h" -#include "core/core_string_names.h" #include "scene/scene_string_names.h" #include "servers/rendering_server.h" @@ -101,13 +100,13 @@ void NinePatchRect::set_texture(const Ref<Texture2D> &p_tex) { } if (texture.is_valid()) { - texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NinePatchRect::_texture_changed)); + texture->disconnect_changed(callable_mp(this, &NinePatchRect::_texture_changed)); } texture = p_tex; if (texture.is_valid()) { - texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NinePatchRect::_texture_changed)); + texture->connect_changed(callable_mp(this, &NinePatchRect::_texture_changed)); } queue_redraw(); diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 71d64c8bff..8138a66f64 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -445,13 +445,11 @@ void OptionButton::_select_int(int p_which) { void OptionButton::_refresh_size_cache() { cache_refresh_pending = false; - if (!fit_to_longest_item) { - return; - } - - _cached_size = Vector2(); - for (int i = 0; i < get_item_count(); i++) { - _cached_size = _cached_size.max(get_minimum_size_for_text_and_icon(popup->get_item_xl_text(i), get_item_icon(i))); + if (fit_to_longest_item) { + _cached_size = Vector2(); + for (int i = 0; i < get_item_count(); i++) { + _cached_size = _cached_size.max(get_minimum_size_for_text_and_icon(popup->get_item_xl_text(i), get_item_icon(i))); + } } update_minimum_size(); } diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index c3c18ddade..40db8deaac 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -1968,7 +1968,7 @@ void PopupMenu::clear() { void PopupMenu::_ref_shortcut(Ref<Shortcut> p_sc) { if (!shortcut_refcount.has(p_sc)) { shortcut_refcount[p_sc] = 1; - p_sc->connect("changed", callable_mp(this, &PopupMenu::_shortcut_changed)); + p_sc->connect_changed(callable_mp(this, &PopupMenu::_shortcut_changed)); } else { shortcut_refcount[p_sc] += 1; } @@ -1978,7 +1978,7 @@ void PopupMenu::_unref_shortcut(Ref<Shortcut> p_sc) { ERR_FAIL_COND(!shortcut_refcount.has(p_sc)); shortcut_refcount[p_sc]--; if (shortcut_refcount[p_sc] == 0) { - p_sc->disconnect("changed", callable_mp(this, &PopupMenu::_shortcut_changed)); + p_sc->disconnect_changed(callable_mp(this, &PopupMenu::_shortcut_changed)); shortcut_refcount.erase(p_sc); } } diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index b07a3d7669..dcbb25c41d 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -30,7 +30,6 @@ #include "texture_button.h" -#include "core/core_string_names.h" #include "core/typedefs.h" #include <stdlib.h> @@ -353,12 +352,12 @@ void TextureButton::_set_texture(Ref<Texture2D> *p_destination, const Ref<Textur return; } if (destination.is_valid()) { - destination->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureButton::_texture_changed)); + destination->disconnect_changed(callable_mp(this, &TextureButton::_texture_changed)); } destination = p_texture; if (destination.is_valid()) { // Pass `CONNECT_REFERENCE_COUNTED` to avoid early disconnect in case the same texture is assigned to different "slots". - destination->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureButton::_texture_changed), CONNECT_REFERENCE_COUNTED); + destination->connect_changed(callable_mp(this, &TextureButton::_texture_changed), CONNECT_REFERENCE_COUNTED); } _texture_changed(); } diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index c43549b9b6..70c2cc9d65 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -31,7 +31,6 @@ #include "texture_progress_bar.h" #include "core/config/engine.h" -#include "core/core_string_names.h" #include "scene/resources/atlas_texture.h" void TextureProgressBar::set_under_texture(const Ref<Texture2D> &p_texture) { @@ -162,12 +161,12 @@ void TextureProgressBar::_set_texture(Ref<Texture2D> *p_destination, const Ref<T return; } if (destination.is_valid()) { - destination->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureProgressBar::_texture_changed)); + destination->disconnect_changed(callable_mp(this, &TextureProgressBar::_texture_changed)); } destination = p_texture; if (destination.is_valid()) { // Pass `CONNECT_REFERENCE_COUNTED` to avoid early disconnect in case the same texture is assigned to different "slots". - destination->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureProgressBar::_texture_changed), CONNECT_REFERENCE_COUNTED); + destination->connect_changed(callable_mp(this, &TextureProgressBar::_texture_changed), CONNECT_REFERENCE_COUNTED); } _texture_changed(); } diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp index 5141847677..d94b11789f 100644 --- a/scene/gui/texture_rect.cpp +++ b/scene/gui/texture_rect.cpp @@ -30,7 +30,6 @@ #include "texture_rect.h" -#include "core/core_string_names.h" #include "scene/resources/atlas_texture.h" #include "servers/rendering_server.h" @@ -202,13 +201,13 @@ void TextureRect::set_texture(const Ref<Texture2D> &p_tex) { } if (texture.is_valid()) { - texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureRect::_texture_changed)); + texture->disconnect_changed(callable_mp(this, &TextureRect::_texture_changed)); } texture = p_tex; if (texture.is_valid()) { - texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureRect::_texture_changed)); + texture->connect_changed(callable_mp(this, &TextureRect::_texture_changed)); } queue_redraw(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 567649aa16..6d2706db1e 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -31,7 +31,6 @@ #include "viewport.h" #include "core/config/project_settings.h" -#include "core/core_string_names.h" #include "core/debugger/engine_debugger.h" #include "core/object/message_queue.h" #include "core/string/translation.h" @@ -3864,7 +3863,7 @@ void Viewport::set_world_3d(const Ref<World3D> &p_world_3d) { } if (own_world_3d.is_valid() && world_3d.is_valid()) { - world_3d->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Viewport::_own_world_3d_changed)); + world_3d->disconnect_changed(callable_mp(this, &Viewport::_own_world_3d_changed)); } world_3d = p_world_3d; @@ -3872,7 +3871,7 @@ void Viewport::set_world_3d(const Ref<World3D> &p_world_3d) { if (own_world_3d.is_valid()) { if (world_3d.is_valid()) { own_world_3d = world_3d->duplicate(); - world_3d->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Viewport::_own_world_3d_changed)); + world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed)); } else { own_world_3d = Ref<World3D>(memnew(World3D)); } @@ -3923,14 +3922,14 @@ void Viewport::set_use_own_world_3d(bool p_use_own_world_3d) { if (p_use_own_world_3d) { if (world_3d.is_valid()) { own_world_3d = world_3d->duplicate(); - world_3d->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Viewport::_own_world_3d_changed)); + world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed)); } else { own_world_3d = Ref<World3D>(memnew(World3D)); } } else { own_world_3d = Ref<World3D>(); if (world_3d.is_valid()) { - world_3d->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Viewport::_own_world_3d_changed)); + world_3d->disconnect_changed(callable_mp(this, &Viewport::_own_world_3d_changed)); } } diff --git a/scene/main/window.cpp b/scene/main/window.cpp index be88b757fd..3ea53da141 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -54,21 +54,21 @@ bool Window::_set(const StringName &p_name, const Variant &p_value) { if (name.begins_with("theme_override_icons/")) { String dname = name.get_slicec('/', 1); if (theme_icon_override.has(dname)) { - theme_icon_override[dname]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); + theme_icon_override[dname]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed)); } theme_icon_override.erase(dname); _notify_theme_override_changed(); } else if (name.begins_with("theme_override_styles/")) { String dname = name.get_slicec('/', 1); if (theme_style_override.has(dname)) { - theme_style_override[dname]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); + theme_style_override[dname]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed)); } theme_style_override.erase(dname); _notify_theme_override_changed(); } else if (name.begins_with("theme_override_fonts/")) { String dname = name.get_slicec('/', 1); if (theme_font_override.has(dname)) { - theme_font_override[dname]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); + theme_font_override[dname]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed)); } theme_font_override.erase(dname); _notify_theme_override_changed(); @@ -1823,13 +1823,13 @@ void Window::set_theme(const Ref<Theme> &p_theme) { } if (theme.is_valid()) { - theme->disconnect("changed", callable_mp(this, &Window::_theme_changed)); + theme->disconnect_changed(callable_mp(this, &Window::_theme_changed)); } theme = p_theme; if (theme.is_valid()) { theme_owner->propagate_theme_changed(this, this, is_inside_tree(), true); - theme->connect("changed", callable_mp(this, &Window::_theme_changed), CONNECT_DEFERRED); + theme->connect_changed(callable_mp(this, &Window::_theme_changed), CONNECT_DEFERRED); return; } @@ -2165,11 +2165,11 @@ void Window::add_theme_icon_override(const StringName &p_name, const Ref<Texture ERR_FAIL_COND(!p_icon.is_valid()); if (theme_icon_override.has(p_name)) { - theme_icon_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); + theme_icon_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed)); } theme_icon_override[p_name] = p_icon; - theme_icon_override[p_name]->connect("changed", callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); + theme_icon_override[p_name]->connect_changed(callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); _notify_theme_override_changed(); } @@ -2178,11 +2178,11 @@ void Window::add_theme_style_override(const StringName &p_name, const Ref<StyleB ERR_FAIL_COND(!p_style.is_valid()); if (theme_style_override.has(p_name)) { - theme_style_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); + theme_style_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed)); } theme_style_override[p_name] = p_style; - theme_style_override[p_name]->connect("changed", callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); + theme_style_override[p_name]->connect_changed(callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); _notify_theme_override_changed(); } @@ -2191,11 +2191,11 @@ void Window::add_theme_font_override(const StringName &p_name, const Ref<Font> & ERR_FAIL_COND(!p_font.is_valid()); if (theme_font_override.has(p_name)) { - theme_font_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); + theme_font_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed)); } theme_font_override[p_name] = p_font; - theme_font_override[p_name]->connect("changed", callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); + theme_font_override[p_name]->connect_changed(callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); _notify_theme_override_changed(); } @@ -2220,7 +2220,7 @@ void Window::add_theme_constant_override(const StringName &p_name, int p_constan void Window::remove_theme_icon_override(const StringName &p_name) { ERR_MAIN_THREAD_GUARD; if (theme_icon_override.has(p_name)) { - theme_icon_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); + theme_icon_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed)); } theme_icon_override.erase(p_name); @@ -2230,7 +2230,7 @@ void Window::remove_theme_icon_override(const StringName &p_name) { void Window::remove_theme_style_override(const StringName &p_name) { ERR_MAIN_THREAD_GUARD; if (theme_style_override.has(p_name)) { - theme_style_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); + theme_style_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed)); } theme_style_override.erase(p_name); @@ -2240,7 +2240,7 @@ void Window::remove_theme_style_override(const StringName &p_name) { void Window::remove_theme_font_override(const StringName &p_name) { ERR_MAIN_THREAD_GUARD; if (theme_font_override.has(p_name)) { - theme_font_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); + theme_font_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed)); } theme_font_override.erase(p_name); @@ -2768,13 +2768,13 @@ Window::~Window() { // Resources need to be disconnected. for (KeyValue<StringName, Ref<Texture2D>> &E : theme_icon_override) { - E.value->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); + E.value->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed)); } for (KeyValue<StringName, Ref<StyleBox>> &E : theme_style_override) { - E.value->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); + E.value->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed)); } for (KeyValue<StringName, Ref<Font>> &E : theme_font_override) { - E.value->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); + E.value->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed)); } // Then override maps can be simply cleared. diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 8a925f8a2e..1c2ed16e63 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -202,6 +202,9 @@ #include "scene/resources/sky_material.h" #include "scene/resources/sphere_shape_3d.h" #include "scene/resources/style_box.h" +#include "scene/resources/style_box_flat.h" +#include "scene/resources/style_box_line.h" +#include "scene/resources/style_box_texture.h" #include "scene/resources/surface_tool.h" #include "scene/resources/syntax_highlighter.h" #include "scene/resources/text_file.h" diff --git a/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp index 206e9df71a..a69b1818d2 100644 --- a/scene/resources/animation_library.cpp +++ b/scene/resources/animation_library.cpp @@ -52,13 +52,13 @@ Error AnimationLibrary::add_animation(const StringName &p_name, const Ref<Animat ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER); if (animations.has(p_name)) { - animations.get(p_name)->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed)); + animations.get(p_name)->disconnect_changed(callable_mp(this, &AnimationLibrary::_animation_changed)); animations.erase(p_name); emit_signal(SNAME("animation_removed"), p_name); } animations.insert(p_name, p_animation); - animations.get(p_name)->connect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed).bind(p_name)); + animations.get(p_name)->connect_changed(callable_mp(this, &AnimationLibrary::_animation_changed).bind(p_name)); emit_signal(SNAME("animation_added"), p_name); notify_property_list_changed(); return OK; @@ -67,7 +67,7 @@ Error AnimationLibrary::add_animation(const StringName &p_name, const Ref<Animat void AnimationLibrary::remove_animation(const StringName &p_name) { ERR_FAIL_COND_MSG(!animations.has(p_name), vformat("Animation not found: %s.", p_name)); - animations.get(p_name)->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed)); + animations.get(p_name)->disconnect_changed(callable_mp(this, &AnimationLibrary::_animation_changed)); animations.erase(p_name); emit_signal(SNAME("animation_removed"), p_name); notify_property_list_changed(); @@ -78,8 +78,8 @@ void AnimationLibrary::rename_animation(const StringName &p_name, const StringNa ERR_FAIL_COND_MSG(!is_valid_animation_name(p_new_name), "Invalid animation name: '" + String(p_new_name) + "'."); ERR_FAIL_COND_MSG(animations.has(p_new_name), vformat("Animation name \"%s\" already exists in library.", p_new_name)); - animations.get(p_name)->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed)); - animations.get(p_name)->connect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed).bind(p_new_name)); + animations.get(p_name)->disconnect_changed(callable_mp(this, &AnimationLibrary::_animation_changed)); + animations.get(p_name)->connect_changed(callable_mp(this, &AnimationLibrary::_animation_changed).bind(p_new_name)); animations.insert(p_new_name, animations[p_name]); animations.erase(p_name); emit_signal(SNAME("animation_renamed"), p_name, p_new_name); @@ -125,7 +125,7 @@ void AnimationLibrary::get_animation_list(List<StringName> *p_animations) const void AnimationLibrary::_set_data(const Dictionary &p_data) { for (KeyValue<StringName, Ref<Animation>> &K : animations) { - K.value->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed)); + K.value->disconnect_changed(callable_mp(this, &AnimationLibrary::_animation_changed)); } animations.clear(); List<Variant> keys; diff --git a/scene/resources/atlas_texture.cpp b/scene/resources/atlas_texture.cpp index e7004ac7a2..24bf25f2db 100644 --- a/scene/resources/atlas_texture.cpp +++ b/scene/resources/atlas_texture.cpp @@ -77,11 +77,11 @@ void AtlasTexture::set_atlas(const Ref<Texture2D> &p_atlas) { } // Support recursive AtlasTextures. if (Ref<AtlasTexture>(atlas).is_valid()) { - atlas->disconnect(CoreStringNames::get_singleton()->changed, callable_mp((Resource *)this, &AtlasTexture::emit_changed)); + atlas->disconnect_changed(callable_mp((Resource *)this, &AtlasTexture::emit_changed)); } atlas = p_atlas; if (Ref<AtlasTexture>(atlas).is_valid()) { - atlas->connect(CoreStringNames::get_singleton()->changed, callable_mp((Resource *)this, &AtlasTexture::emit_changed)); + atlas->connect_changed(callable_mp((Resource *)this, &AtlasTexture::emit_changed)); } emit_changed(); diff --git a/scene/resources/box_shape_3d.cpp b/scene/resources/box_shape_3d.cpp index 6a1f7dc6ea..313aeb1bca 100644 --- a/scene/resources/box_shape_3d.cpp +++ b/scene/resources/box_shape_3d.cpp @@ -80,7 +80,7 @@ void BoxShape3D::set_size(const Vector3 &p_size) { ERR_FAIL_COND_MSG(p_size.x < 0 || p_size.y < 0 || p_size.z < 0, "BoxShape3D size cannot be negative."); size = p_size; _update_shape(); - notify_change_to_owners(); + emit_changed(); } Vector3 BoxShape3D::get_size() const { diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp index 8f4f804397..7c46729af3 100644 --- a/scene/resources/camera_attributes.cpp +++ b/scene/resources/camera_attributes.cpp @@ -122,7 +122,7 @@ void CameraAttributes::_bind_methods() { ADD_GROUP("Exposure", "exposure_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_sensitivity", PROPERTY_HINT_RANGE, "0.1,32000.0,0.1,suffix:ISO"), "set_exposure_sensitivity", "get_exposure_sensitivity"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_multiplier", PROPERTY_HINT_RANGE, "0.0,2048.0,0.001"), "set_exposure_multiplier", "get_exposure_multiplier"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_multiplier", PROPERTY_HINT_RANGE, "0.0,8.0,0.001,or_greater"), "set_exposure_multiplier", "get_exposure_multiplier"); ADD_GROUP("Auto Exposure", "auto_exposure_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_exposure_enabled"), "set_auto_exposure_enabled", "is_auto_exposure_enabled"); diff --git a/scene/resources/capsule_shape_3d.cpp b/scene/resources/capsule_shape_3d.cpp index 3edff0cc68..9e16801060 100644 --- a/scene/resources/capsule_shape_3d.cpp +++ b/scene/resources/capsule_shape_3d.cpp @@ -86,7 +86,7 @@ void CapsuleShape3D::set_radius(float p_radius) { height = radius * 2.0; } _update_shape(); - notify_change_to_owners(); + emit_changed(); } float CapsuleShape3D::get_radius() const { @@ -100,7 +100,7 @@ void CapsuleShape3D::set_height(float p_height) { radius = height * 0.5; } _update_shape(); - notify_change_to_owners(); + emit_changed(); } float CapsuleShape3D::get_height() const { diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp index cb5e0fc600..82b125905f 100644 --- a/scene/resources/concave_polygon_shape_3d.cpp +++ b/scene/resources/concave_polygon_shape_3d.cpp @@ -81,7 +81,7 @@ void ConcavePolygonShape3D::_update_shape() { void ConcavePolygonShape3D::set_faces(const Vector<Vector3> &p_faces) { faces = p_faces; _update_shape(); - notify_change_to_owners(); + emit_changed(); } Vector<Vector3> ConcavePolygonShape3D::get_faces() const { @@ -93,7 +93,7 @@ void ConcavePolygonShape3D::set_backface_collision_enabled(bool p_enabled) { if (!faces.is_empty()) { _update_shape(); - notify_change_to_owners(); + emit_changed(); } } diff --git a/scene/resources/convex_polygon_shape_3d.cpp b/scene/resources/convex_polygon_shape_3d.cpp index cc0ef8f583..3bfeeca461 100644 --- a/scene/resources/convex_polygon_shape_3d.cpp +++ b/scene/resources/convex_polygon_shape_3d.cpp @@ -71,7 +71,7 @@ void ConvexPolygonShape3D::_update_shape() { void ConvexPolygonShape3D::set_points(const Vector<Vector3> &p_points) { points = p_points; _update_shape(); - notify_change_to_owners(); + emit_changed(); } Vector<Vector3> ConvexPolygonShape3D::get_points() const { diff --git a/scene/resources/curve_texture.cpp b/scene/resources/curve_texture.cpp index 61bd7b1e6f..3578b46308 100644 --- a/scene/resources/curve_texture.cpp +++ b/scene/resources/curve_texture.cpp @@ -81,11 +81,11 @@ void CurveTexture::ensure_default_setup(float p_min, float p_max) { void CurveTexture::set_curve(Ref<Curve> p_curve) { if (_curve != p_curve) { if (_curve.is_valid()) { - _curve->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveTexture::_update)); + _curve->disconnect_changed(callable_mp(this, &CurveTexture::_update)); } _curve = p_curve; if (_curve.is_valid()) { - _curve->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveTexture::_update)); + _curve->connect_changed(callable_mp(this, &CurveTexture::_update)); } _update(); } @@ -245,11 +245,11 @@ void CurveXYZTexture::ensure_default_setup(float p_min, float p_max) { void CurveXYZTexture::set_curve_x(Ref<Curve> p_curve) { if (_curve_x != p_curve) { if (_curve_x.is_valid()) { - _curve_x->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update)); + _curve_x->disconnect_changed(callable_mp(this, &CurveXYZTexture::_update)); } _curve_x = p_curve; if (_curve_x.is_valid()) { - _curve_x->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED); + _curve_x->connect_changed(callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED); } _update(); } @@ -258,11 +258,11 @@ void CurveXYZTexture::set_curve_x(Ref<Curve> p_curve) { void CurveXYZTexture::set_curve_y(Ref<Curve> p_curve) { if (_curve_y != p_curve) { if (_curve_y.is_valid()) { - _curve_y->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update)); + _curve_y->disconnect_changed(callable_mp(this, &CurveXYZTexture::_update)); } _curve_y = p_curve; if (_curve_y.is_valid()) { - _curve_y->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED); + _curve_y->connect_changed(callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED); } _update(); } @@ -271,11 +271,11 @@ void CurveXYZTexture::set_curve_y(Ref<Curve> p_curve) { void CurveXYZTexture::set_curve_z(Ref<Curve> p_curve) { if (_curve_z != p_curve) { if (_curve_z.is_valid()) { - _curve_z->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update)); + _curve_z->disconnect_changed(callable_mp(this, &CurveXYZTexture::_update)); } _curve_z = p_curve; if (_curve_z.is_valid()) { - _curve_z->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED); + _curve_z->connect_changed(callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED); } _update(); } diff --git a/scene/resources/cylinder_shape_3d.cpp b/scene/resources/cylinder_shape_3d.cpp index aee2963b2e..a91282fd33 100644 --- a/scene/resources/cylinder_shape_3d.cpp +++ b/scene/resources/cylinder_shape_3d.cpp @@ -76,7 +76,7 @@ void CylinderShape3D::set_radius(float p_radius) { ERR_FAIL_COND_MSG(p_radius < 0, "CylinderShape3D radius cannot be negative."); radius = p_radius; _update_shape(); - notify_change_to_owners(); + emit_changed(); } float CylinderShape3D::get_radius() const { @@ -87,7 +87,7 @@ void CylinderShape3D::set_height(float p_height) { ERR_FAIL_COND_MSG(p_height < 0, "CylinderShape3D height cannot be negative."); height = p_height; _update_shape(); - notify_change_to_owners(); + emit_changed(); } float CylinderShape3D::get_height() const { diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 20424b06f4..24ffaca68d 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -36,6 +36,8 @@ #include "scene/resources/font.h" #include "scene/resources/gradient_texture.h" #include "scene/resources/image_texture.h" +#include "scene/resources/style_box_flat.h" +#include "scene/resources/style_box_line.h" #include "scene/resources/theme.h" #include "scene/theme/theme_db.h" #include "servers/text_server.h" diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index bfb1a31632..e48f744c72 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -31,7 +31,6 @@ #include "environment.h" #include "core/config/project_settings.h" -#include "core/core_string_names.h" #include "scene/resources/gradient_texture.h" #include "servers/rendering_server.h" @@ -1004,9 +1003,7 @@ void Environment::set_adjustment_color_correction(Ref<Texture> p_color_correctio adjustment_color_correction = p_color_correction; Ref<GradientTexture1D> grad_tex = p_color_correction; if (grad_tex.is_valid()) { - if (!grad_tex->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment))) { - grad_tex->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment)); - } + grad_tex->connect_changed(callable_mp(this, &Environment::_update_adjustment)); } Ref<Texture2D> adjustment_texture_2d = adjustment_color_correction; if (adjustment_texture_2d.is_valid()) { diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 454ff5e222..09a835db1b 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -30,7 +30,6 @@ #include "font.h" -#include "core/core_string_names.h" #include "core/io/image_loader.h" #include "core/io/resource_loader.h" #include "core/string/translation.h" @@ -160,14 +159,14 @@ void Font::set_fallbacks(const TypedArray<Font> &p_fallbacks) { for (int i = 0; i < fallbacks.size(); i++) { Ref<Font> f = fallbacks[i]; if (f.is_valid()) { - f->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids)); + f->disconnect_changed(callable_mp(this, &Font::_invalidate_rids)); } } fallbacks = p_fallbacks; for (int i = 0; i < fallbacks.size(); i++) { Ref<Font> f = fallbacks[i]; if (f.is_valid()) { - f->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + f->connect_changed(callable_mp(this, &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } } _invalidate_rids(); @@ -2675,12 +2674,12 @@ void FontVariation::_update_rids() const { void FontVariation::reset_state() { if (base_font.is_valid()) { - base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + base_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); base_font.unref(); } if (theme_font.is_valid()) { - theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + theme_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); theme_font.unref(); } @@ -2697,11 +2696,11 @@ void FontVariation::reset_state() { void FontVariation::set_base_font(const Ref<Font> &p_font) { if (base_font != p_font) { if (base_font.is_valid()) { - base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + base_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); } base_font = p_font; if (base_font.is_valid()) { - base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + base_font->connect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } _invalidate_rids(); notify_property_list_changed(); @@ -2714,7 +2713,7 @@ Ref<Font> FontVariation::get_base_font() const { Ref<Font> FontVariation::_get_base_font_or_default() const { if (theme_font.is_valid()) { - theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids)); + theme_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids)); theme_font.unref(); } @@ -2735,7 +2734,7 @@ Ref<Font> FontVariation::_get_base_font_or_default() const { } if (f.is_valid()) { theme_font = f; - theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } return f; } @@ -2755,7 +2754,7 @@ Ref<Font> FontVariation::_get_base_font_or_default() const { } if (f.is_valid()) { theme_font = f; - theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } return f; } @@ -2766,7 +2765,7 @@ Ref<Font> FontVariation::_get_base_font_or_default() const { if (f != this) { if (f.is_valid()) { theme_font = f; - theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } return f; } @@ -2950,7 +2949,7 @@ void SystemFont::_update_rids() const { void SystemFont::_update_base_font() { if (base_font.is_valid()) { - base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + base_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); base_font.unref(); } @@ -3031,7 +3030,7 @@ void SystemFont::_update_base_font() { } if (base_font.is_valid()) { - base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + base_font->connect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } _invalidate_rids(); @@ -3040,12 +3039,12 @@ void SystemFont::_update_base_font() { void SystemFont::reset_state() { if (base_font.is_valid()) { - base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + base_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); base_font.unref(); } if (theme_font.is_valid()) { - theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + theme_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); theme_font.unref(); } @@ -3071,7 +3070,7 @@ void SystemFont::reset_state() { Ref<Font> SystemFont::_get_base_font_or_default() const { if (theme_font.is_valid()) { - theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids)); + theme_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids)); theme_font.unref(); } @@ -3092,7 +3091,7 @@ Ref<Font> SystemFont::_get_base_font_or_default() const { } if (f.is_valid()) { theme_font = f; - theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } return f; } @@ -3112,7 +3111,7 @@ Ref<Font> SystemFont::_get_base_font_or_default() const { } if (f.is_valid()) { theme_font = f; - theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } return f; } @@ -3123,7 +3122,7 @@ Ref<Font> SystemFont::_get_base_font_or_default() const { if (f != this) { if (f.is_valid()) { theme_font = f; - theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } return f; } diff --git a/scene/resources/gradient_texture.cpp b/scene/resources/gradient_texture.cpp index 322353b66c..20868faaa2 100644 --- a/scene/resources/gradient_texture.cpp +++ b/scene/resources/gradient_texture.cpp @@ -66,11 +66,11 @@ void GradientTexture1D::set_gradient(Ref<Gradient> p_gradient) { return; } if (gradient.is_valid()) { - gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture1D::_update)); + gradient->disconnect_changed(callable_mp(this, &GradientTexture1D::_update)); } gradient = p_gradient; if (gradient.is_valid()) { - gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture1D::_update)); + gradient->connect_changed(callable_mp(this, &GradientTexture1D::_update)); } _update(); emit_changed(); @@ -192,11 +192,11 @@ void GradientTexture2D::set_gradient(Ref<Gradient> p_gradient) { return; } if (gradient.is_valid()) { - gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture2D::_queue_update)); + gradient->disconnect_changed(callable_mp(this, &GradientTexture2D::_queue_update)); } gradient = p_gradient; if (gradient.is_valid()) { - gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture2D::_queue_update)); + gradient->connect_changed(callable_mp(this, &GradientTexture2D::_queue_update)); } _update(); emit_changed(); diff --git a/scene/resources/height_map_shape_3d.cpp b/scene/resources/height_map_shape_3d.cpp index 553daa93e6..718d701811 100644 --- a/scene/resources/height_map_shape_3d.cpp +++ b/scene/resources/height_map_shape_3d.cpp @@ -112,7 +112,7 @@ void HeightMapShape3D::set_map_width(int p_new) { } _update_shape(); - notify_change_to_owners(); + emit_changed(); } } @@ -136,7 +136,7 @@ void HeightMapShape3D::set_map_depth(int p_new) { } _update_shape(); - notify_change_to_owners(); + emit_changed(); } } @@ -172,7 +172,7 @@ void HeightMapShape3D::set_map_data(Vector<real_t> p_new) { } _update_shape(); - notify_change_to_owners(); + emit_changed(); } Vector<real_t> HeightMapShape3D::get_map_data() const { diff --git a/scene/resources/label_settings.cpp b/scene/resources/label_settings.cpp index 37912df921..9a530fb680 100644 --- a/scene/resources/label_settings.cpp +++ b/scene/resources/label_settings.cpp @@ -30,8 +30,6 @@ #include "label_settings.h" -#include "core/core_string_names.h" - void LabelSettings::_font_changed() { emit_changed(); } @@ -95,11 +93,11 @@ real_t LabelSettings::get_line_spacing() const { void LabelSettings::set_font(const Ref<Font> &p_font) { if (font != p_font) { if (font.is_valid()) { - font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &LabelSettings::_font_changed)); + font->disconnect_changed(callable_mp(this, &LabelSettings::_font_changed)); } font = p_font; if (font.is_valid()) { - font->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &LabelSettings::_font_changed), CONNECT_REFERENCE_COUNTED); + font->connect_changed(callable_mp(this, &LabelSettings::_font_changed), CONNECT_REFERENCE_COUNTED); } emit_changed(); } diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index a2aab8e7b4..d3ef4a303b 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -385,7 +385,7 @@ void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) { // This can be a slow operation, and `notify_property_list_changed()` (which is called by `_shader_changed()`) // does nothing in non-editor builds anyway. See GH-34741 for details. if (shader.is_valid() && Engine::get_singleton()->is_editor_hint()) { - shader->disconnect("changed", callable_mp(this, &ShaderMaterial::_shader_changed)); + shader->disconnect_changed(callable_mp(this, &ShaderMaterial::_shader_changed)); } shader = p_shader; @@ -395,7 +395,7 @@ void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) { rid = shader->get_rid(); if (Engine::get_singleton()->is_editor_hint()) { - shader->connect("changed", callable_mp(this, &ShaderMaterial::_shader_changed)); + shader->connect_changed(callable_mp(this, &ShaderMaterial::_shader_changed)); } } diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index 015eb0fa45..5000541621 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -144,7 +144,6 @@ void MeshLibrary::set_item_name(int p_item, const String &p_name) { void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].mesh = p_mesh; - notify_change_to_owners(); emit_changed(); notify_property_list_changed(); } @@ -152,7 +151,6 @@ void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) { void MeshLibrary::set_item_mesh_transform(int p_item, const Transform3D &p_transform) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].mesh_transform = p_transform; - notify_change_to_owners(); emit_changed(); } @@ -160,7 +158,6 @@ void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].shapes = p_shapes; notify_property_list_changed(); - notify_change_to_owners(); emit_changed(); notify_property_list_changed(); } @@ -169,7 +166,6 @@ void MeshLibrary::set_item_navigation_mesh(int p_item, const Ref<NavigationMesh> ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].navigation_mesh = p_navigation_mesh; notify_property_list_changed(); - notify_change_to_owners(); emit_changed(); notify_property_list_changed(); } @@ -177,7 +173,6 @@ void MeshLibrary::set_item_navigation_mesh(int p_item, const Ref<NavigationMesh> void MeshLibrary::set_item_navigation_mesh_transform(int p_item, const Transform3D &p_transform) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].navigation_mesh_transform = p_transform; - notify_change_to_owners(); emit_changed(); notify_property_list_changed(); } @@ -186,7 +181,6 @@ void MeshLibrary::set_item_navigation_layers(int p_item, uint32_t p_navigation_l ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].navigation_layers = p_navigation_layers; notify_property_list_changed(); - notify_change_to_owners(); emit_changed(); } @@ -244,14 +238,12 @@ bool MeshLibrary::has_item(int p_item) const { void MeshLibrary::remove_item(int p_item) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map.erase(p_item); - notify_change_to_owners(); notify_property_list_changed(); emit_changed(); } void MeshLibrary::clear() { item_map.clear(); - notify_change_to_owners(); notify_property_list_changed(); emit_changed(); } diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 204987cac9..6430a1302d 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -31,7 +31,6 @@ #include "primitive_meshes.h" #include "core/config/project_settings.h" -#include "core/core_string_names.h" #include "scene/resources/theme.h" #include "scene/theme/theme_db.h" #include "servers/rendering_server.h" @@ -2194,11 +2193,11 @@ void TubeTrailMesh::set_curve(const Ref<Curve> &p_curve) { return; } if (curve.is_valid()) { - curve->disconnect("changed", callable_mp(this, &TubeTrailMesh::_curve_changed)); + curve->disconnect_changed(callable_mp(this, &TubeTrailMesh::_curve_changed)); } curve = p_curve; if (curve.is_valid()) { - curve->connect("changed", callable_mp(this, &TubeTrailMesh::_curve_changed)); + curve->connect_changed(callable_mp(this, &TubeTrailMesh::_curve_changed)); } _request_update(); } @@ -2533,11 +2532,11 @@ void RibbonTrailMesh::set_curve(const Ref<Curve> &p_curve) { return; } if (curve.is_valid()) { - curve->disconnect("changed", callable_mp(this, &RibbonTrailMesh::_curve_changed)); + curve->disconnect_changed(callable_mp(this, &RibbonTrailMesh::_curve_changed)); } curve = p_curve; if (curve.is_valid()) { - curve->connect("changed", callable_mp(this, &RibbonTrailMesh::_curve_changed)); + curve->connect_changed(callable_mp(this, &RibbonTrailMesh::_curve_changed)); } _request_update(); } @@ -3446,13 +3445,13 @@ void TextMesh::_font_changed() { void TextMesh::set_font(const Ref<Font> &p_font) { if (font_override != p_font) { if (font_override.is_valid()) { - font_override->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed")); + font_override->disconnect_changed(Callable(this, "_font_changed")); } font_override = p_font; dirty_font = true; dirty_cache = true; if (font_override.is_valid()) { - font_override->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed")); + font_override->connect_changed(Callable(this, "_font_changed")); } _request_update(); } diff --git a/scene/resources/separation_ray_shape_3d.cpp b/scene/resources/separation_ray_shape_3d.cpp index 3d8e2af201..07e93b8b79 100644 --- a/scene/resources/separation_ray_shape_3d.cpp +++ b/scene/resources/separation_ray_shape_3d.cpp @@ -56,7 +56,7 @@ void SeparationRayShape3D::_update_shape() { void SeparationRayShape3D::set_length(float p_length) { length = p_length; _update_shape(); - notify_change_to_owners(); + emit_changed(); } float SeparationRayShape3D::get_length() const { @@ -66,7 +66,7 @@ float SeparationRayShape3D::get_length() const { void SeparationRayShape3D::set_slide_on_slope(bool p_active) { slide_on_slope = p_active; _update_shape(); - notify_change_to_owners(); + emit_changed(); } bool SeparationRayShape3D::get_slide_on_slope() const { @@ -88,5 +88,5 @@ SeparationRayShape3D::SeparationRayShape3D() : Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_SEPARATION_RAY)) { /* Code copied from setters to prevent the use of uninitialized variables */ _update_shape(); - notify_change_to_owners(); + emit_changed(); } diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 361a28194f..f919386980 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -63,7 +63,7 @@ void Shader::set_include_path(const String &p_path) { void Shader::set_code(const String &p_code) { for (const Ref<ShaderInclude> &E : include_dependencies) { - E->disconnect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed)); + E->disconnect_changed(callable_mp(this, &Shader::_dependency_changed)); } code = p_code; @@ -102,7 +102,7 @@ void Shader::set_code(const String &p_code) { } for (const Ref<ShaderInclude> &E : include_dependencies) { - E->connect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed)); + E->connect_changed(callable_mp(this, &Shader::_dependency_changed)); } RenderingServer::get_singleton()->shader_set_code(shader, pp_code); diff --git a/scene/resources/shader_include.cpp b/scene/resources/shader_include.cpp index 6e9c64d34b..aead484cc1 100644 --- a/scene/resources/shader_include.cpp +++ b/scene/resources/shader_include.cpp @@ -40,7 +40,7 @@ void ShaderInclude::set_code(const String &p_code) { code = p_code; for (const Ref<ShaderInclude> &E : dependencies) { - E->disconnect(SNAME("changed"), callable_mp(this, &ShaderInclude::_dependency_changed)); + E->disconnect_changed(callable_mp(this, &ShaderInclude::_dependency_changed)); } { @@ -60,7 +60,7 @@ void ShaderInclude::set_code(const String &p_code) { } for (const Ref<ShaderInclude> &E : dependencies) { - E->connect(SNAME("changed"), callable_mp(this, &ShaderInclude::_dependency_changed)); + E->connect_changed(callable_mp(this, &ShaderInclude::_dependency_changed)); } emit_changed(); diff --git a/scene/resources/sphere_shape_3d.cpp b/scene/resources/sphere_shape_3d.cpp index fa22aabe5b..56b78471ec 100644 --- a/scene/resources/sphere_shape_3d.cpp +++ b/scene/resources/sphere_shape_3d.cpp @@ -67,7 +67,7 @@ void SphereShape3D::set_radius(float p_radius) { ERR_FAIL_COND_MSG(p_radius < 0, "SphereShape3D radius cannot be negative."); radius = p_radius; _update_shape(); - notify_change_to_owners(); + emit_changed(); } float SphereShape3D::get_radius() const { diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index a9ebb21345..f87bf1ee05 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -32,8 +32,6 @@ #include "scene/main/canvas_item.h" -#include <limits.h> - Size2 StyleBox::get_minimum_size() const { Size2 min_size = Size2(get_margin(SIDE_LEFT) + get_margin(SIDE_RIGHT), get_margin(SIDE_TOP) + get_margin(SIDE_BOTTOM)); Size2 custom_size; @@ -145,925 +143,3 @@ StyleBox::StyleBox() { content_margin[i] = -1; } } - -void StyleBoxTexture::set_texture(Ref<Texture2D> p_texture) { - if (texture == p_texture) { - return; - } - texture = p_texture; - emit_changed(); -} - -Ref<Texture2D> StyleBoxTexture::get_texture() const { - return texture; -} - -void StyleBoxTexture::set_texture_margin(Side p_side, float p_size) { - ERR_FAIL_INDEX((int)p_side, 4); - - texture_margin[p_side] = p_size; - emit_changed(); -} - -void StyleBoxTexture::set_texture_margin_all(float p_size) { - for (int i = 0; i < 4; i++) { - texture_margin[i] = p_size; - } - emit_changed(); -} - -void StyleBoxTexture::set_texture_margin_individual(float p_left, float p_top, float p_right, float p_bottom) { - texture_margin[SIDE_LEFT] = p_left; - texture_margin[SIDE_TOP] = p_top; - texture_margin[SIDE_RIGHT] = p_right; - texture_margin[SIDE_BOTTOM] = p_bottom; - emit_changed(); -} - -float StyleBoxTexture::get_texture_margin(Side p_side) const { - ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); - - return texture_margin[p_side]; -} - -float StyleBoxTexture::get_style_margin(Side p_side) const { - ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); - - return texture_margin[p_side]; -} - -Rect2 StyleBoxTexture::get_draw_rect(const Rect2 &p_rect) const { - return p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]); -} - -void StyleBoxTexture::draw(RID p_canvas_item, const Rect2 &p_rect) const { - if (texture.is_null()) { - return; - } - - Rect2 rect = p_rect; - Rect2 src_rect = region_rect; - - texture->get_rect_region(rect, src_rect, rect, src_rect); - - rect.position.x -= expand_margin[SIDE_LEFT]; - rect.position.y -= expand_margin[SIDE_TOP]; - rect.size.x += expand_margin[SIDE_LEFT] + expand_margin[SIDE_RIGHT]; - rect.size.y += expand_margin[SIDE_TOP] + expand_margin[SIDE_BOTTOM]; - - Vector2 start_offset = Vector2(texture_margin[SIDE_LEFT], texture_margin[SIDE_TOP]); - Vector2 end_offset = Vector2(texture_margin[SIDE_RIGHT], texture_margin[SIDE_BOTTOM]); - - RenderingServer::get_singleton()->canvas_item_add_nine_patch(p_canvas_item, rect, src_rect, texture->get_rid(), start_offset, end_offset, RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center, modulate); -} - -void StyleBoxTexture::set_draw_center(bool p_enabled) { - draw_center = p_enabled; - emit_changed(); -} - -bool StyleBoxTexture::is_draw_center_enabled() const { - return draw_center; -} - -void StyleBoxTexture::set_expand_margin(Side p_side, float p_size) { - ERR_FAIL_INDEX((int)p_side, 4); - expand_margin[p_side] = p_size; - emit_changed(); -} - -void StyleBoxTexture::set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom) { - expand_margin[SIDE_LEFT] = p_left; - expand_margin[SIDE_TOP] = p_top; - expand_margin[SIDE_RIGHT] = p_right; - expand_margin[SIDE_BOTTOM] = p_bottom; - emit_changed(); -} - -void StyleBoxTexture::set_expand_margin_all(float p_expand_margin_size) { - for (int i = 0; i < 4; i++) { - expand_margin[i] = p_expand_margin_size; - } - emit_changed(); -} - -float StyleBoxTexture::get_expand_margin(Side p_side) const { - ERR_FAIL_INDEX_V((int)p_side, 4, 0); - return expand_margin[p_side]; -} - -void StyleBoxTexture::set_region_rect(const Rect2 &p_region_rect) { - if (region_rect == p_region_rect) { - return; - } - - region_rect = p_region_rect; - emit_changed(); -} - -Rect2 StyleBoxTexture::get_region_rect() const { - return region_rect; -} - -void StyleBoxTexture::set_h_axis_stretch_mode(AxisStretchMode p_mode) { - ERR_FAIL_INDEX((int)p_mode, 3); - axis_h = p_mode; - emit_changed(); -} - -StyleBoxTexture::AxisStretchMode StyleBoxTexture::get_h_axis_stretch_mode() const { - return axis_h; -} - -void StyleBoxTexture::set_v_axis_stretch_mode(AxisStretchMode p_mode) { - ERR_FAIL_INDEX((int)p_mode, 3); - axis_v = p_mode; - emit_changed(); -} - -StyleBoxTexture::AxisStretchMode StyleBoxTexture::get_v_axis_stretch_mode() const { - return axis_v; -} - -void StyleBoxTexture::set_modulate(const Color &p_modulate) { - if (modulate == p_modulate) { - return; - } - modulate = p_modulate; - emit_changed(); -} - -Color StyleBoxTexture::get_modulate() const { - return modulate; -} - -void StyleBoxTexture::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_texture", "texture"), &StyleBoxTexture::set_texture); - ClassDB::bind_method(D_METHOD("get_texture"), &StyleBoxTexture::get_texture); - - ClassDB::bind_method(D_METHOD("set_texture_margin", "margin", "size"), &StyleBoxTexture::set_texture_margin); - ClassDB::bind_method(D_METHOD("set_texture_margin_all", "size"), &StyleBoxTexture::set_texture_margin_all); - ClassDB::bind_method(D_METHOD("get_texture_margin", "margin"), &StyleBoxTexture::get_texture_margin); - - ClassDB::bind_method(D_METHOD("set_expand_margin", "margin", "size"), &StyleBoxTexture::set_expand_margin); - ClassDB::bind_method(D_METHOD("set_expand_margin_all", "size"), &StyleBoxTexture::set_expand_margin_all); - ClassDB::bind_method(D_METHOD("get_expand_margin", "margin"), &StyleBoxTexture::get_expand_margin); - - ClassDB::bind_method(D_METHOD("set_region_rect", "region"), &StyleBoxTexture::set_region_rect); - ClassDB::bind_method(D_METHOD("get_region_rect"), &StyleBoxTexture::get_region_rect); - - ClassDB::bind_method(D_METHOD("set_draw_center", "enable"), &StyleBoxTexture::set_draw_center); - ClassDB::bind_method(D_METHOD("is_draw_center_enabled"), &StyleBoxTexture::is_draw_center_enabled); - - ClassDB::bind_method(D_METHOD("set_modulate", "color"), &StyleBoxTexture::set_modulate); - ClassDB::bind_method(D_METHOD("get_modulate"), &StyleBoxTexture::get_modulate); - - ClassDB::bind_method(D_METHOD("set_h_axis_stretch_mode", "mode"), &StyleBoxTexture::set_h_axis_stretch_mode); - ClassDB::bind_method(D_METHOD("get_h_axis_stretch_mode"), &StyleBoxTexture::get_h_axis_stretch_mode); - - ClassDB::bind_method(D_METHOD("set_v_axis_stretch_mode", "mode"), &StyleBoxTexture::set_v_axis_stretch_mode); - ClassDB::bind_method(D_METHOD("get_v_axis_stretch_mode"), &StyleBoxTexture::get_v_axis_stretch_mode); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); - - ADD_GROUP("Texture Margins", "texture_margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_BOTTOM); - - ADD_GROUP("Expand Margins", "expand_margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM); - - ADD_GROUP("Axis Stretch", "axis_stretch_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode"); - - ADD_GROUP("Sub-Region", "region_"); - ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect", PROPERTY_HINT_NONE, "suffix:px"), "set_region_rect", "get_region_rect"); - - ADD_GROUP("Modulate", "modulate_"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate_color"), "set_modulate", "get_modulate"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled"); - - BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_STRETCH); - BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_TILE); - BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_TILE_FIT); -} - -StyleBoxTexture::StyleBoxTexture() {} - -StyleBoxTexture::~StyleBoxTexture() {} - -//////////////// - -void StyleBoxFlat::set_bg_color(const Color &p_color) { - bg_color = p_color; - emit_changed(); -} - -Color StyleBoxFlat::get_bg_color() const { - return bg_color; -} - -void StyleBoxFlat::set_border_color(const Color &p_color) { - border_color = p_color; - emit_changed(); -} - -Color StyleBoxFlat::get_border_color() const { - return border_color; -} - -void StyleBoxFlat::set_border_width_all(int p_size) { - border_width[0] = p_size; - border_width[1] = p_size; - border_width[2] = p_size; - border_width[3] = p_size; - emit_changed(); -} - -int StyleBoxFlat::get_border_width_min() const { - return MIN(MIN(border_width[0], border_width[1]), MIN(border_width[2], border_width[3])); -} - -void StyleBoxFlat::set_border_width(Side p_side, int p_width) { - ERR_FAIL_INDEX((int)p_side, 4); - border_width[p_side] = p_width; - emit_changed(); -} - -int StyleBoxFlat::get_border_width(Side p_side) const { - ERR_FAIL_INDEX_V((int)p_side, 4, 0); - return border_width[p_side]; -} - -void StyleBoxFlat::set_border_blend(bool p_blend) { - blend_border = p_blend; - emit_changed(); -} - -bool StyleBoxFlat::get_border_blend() const { - return blend_border; -} - -void StyleBoxFlat::set_corner_radius_all(int radius) { - for (int i = 0; i < 4; i++) { - corner_radius[i] = radius; - } - - emit_changed(); -} - -void StyleBoxFlat::set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_bottom_right, const int radius_bottom_left) { - corner_radius[0] = radius_top_left; - corner_radius[1] = radius_top_right; - corner_radius[2] = radius_bottom_right; - corner_radius[3] = radius_bottom_left; - - emit_changed(); -} - -void StyleBoxFlat::set_corner_radius(const Corner p_corner, const int radius) { - ERR_FAIL_INDEX((int)p_corner, 4); - corner_radius[p_corner] = radius; - emit_changed(); -} - -int StyleBoxFlat::get_corner_radius(const Corner p_corner) const { - ERR_FAIL_INDEX_V((int)p_corner, 4, 0); - return corner_radius[p_corner]; -} - -void StyleBoxFlat::set_expand_margin(Side p_side, float p_size) { - ERR_FAIL_INDEX((int)p_side, 4); - expand_margin[p_side] = p_size; - emit_changed(); -} - -void StyleBoxFlat::set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom) { - expand_margin[SIDE_LEFT] = p_left; - expand_margin[SIDE_TOP] = p_top; - expand_margin[SIDE_RIGHT] = p_right; - expand_margin[SIDE_BOTTOM] = p_bottom; - emit_changed(); -} - -void StyleBoxFlat::set_expand_margin_all(float p_expand_margin_size) { - for (int i = 0; i < 4; i++) { - expand_margin[i] = p_expand_margin_size; - } - emit_changed(); -} - -float StyleBoxFlat::get_expand_margin(Side p_side) const { - ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); - return expand_margin[p_side]; -} - -void StyleBoxFlat::set_draw_center(bool p_enabled) { - draw_center = p_enabled; - emit_changed(); -} - -bool StyleBoxFlat::is_draw_center_enabled() const { - return draw_center; -} - -void StyleBoxFlat::set_skew(Vector2 p_skew) { - skew = p_skew; - emit_changed(); -} - -Vector2 StyleBoxFlat::get_skew() const { - return skew; -} - -void StyleBoxFlat::set_shadow_color(const Color &p_color) { - shadow_color = p_color; - emit_changed(); -} - -Color StyleBoxFlat::get_shadow_color() const { - return shadow_color; -} - -void StyleBoxFlat::set_shadow_size(const int &p_size) { - shadow_size = p_size; - emit_changed(); -} - -int StyleBoxFlat::get_shadow_size() const { - return shadow_size; -} - -void StyleBoxFlat::set_shadow_offset(const Point2 &p_offset) { - shadow_offset = p_offset; - emit_changed(); -} - -Point2 StyleBoxFlat::get_shadow_offset() const { - return shadow_offset; -} - -void StyleBoxFlat::set_anti_aliased(const bool &p_anti_aliased) { - anti_aliased = p_anti_aliased; - emit_changed(); - notify_property_list_changed(); -} - -bool StyleBoxFlat::is_anti_aliased() const { - return anti_aliased; -} - -void StyleBoxFlat::set_aa_size(const real_t p_aa_size) { - aa_size = CLAMP(p_aa_size, 0.01, 10); - emit_changed(); -} - -real_t StyleBoxFlat::get_aa_size() const { - return aa_size; -} - -void StyleBoxFlat::set_corner_detail(const int &p_corner_detail) { - corner_detail = CLAMP(p_corner_detail, 1, 20); - emit_changed(); -} - -int StyleBoxFlat::get_corner_detail() const { - return corner_detail; -} - -inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_rect, const real_t corner_radius[4], real_t *inner_corner_radius) { - real_t border_left = inner_rect.position.x - style_rect.position.x; - real_t border_top = inner_rect.position.y - style_rect.position.y; - real_t border_right = style_rect.size.width - inner_rect.size.width - border_left; - real_t border_bottom = style_rect.size.height - inner_rect.size.height - border_top; - - real_t rad; - - // Top left. - rad = MIN(border_top, border_left); - inner_corner_radius[0] = MAX(corner_radius[0] - rad, 0); - - // Top right; - rad = MIN(border_top, border_right); - inner_corner_radius[1] = MAX(corner_radius[1] - rad, 0); - - // Bottom right. - rad = MIN(border_bottom, border_right); - inner_corner_radius[2] = MAX(corner_radius[2] - rad, 0); - - // Bottom left. - rad = MIN(border_bottom, border_left); - inner_corner_radius[3] = MAX(corner_radius[3] - rad, 0); -} - -inline void draw_rounded_rectangle(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const real_t corner_radius[4], - const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const Vector2 &skew, bool is_filled = false) { - int vert_offset = verts.size(); - if (!vert_offset) { - vert_offset = 0; - } - - int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail; - - bool draw_border = !is_filled; - - real_t ring_corner_radius[4]; - set_inner_corner_radius(style_rect, ring_rect, corner_radius, ring_corner_radius); - - // Corner radius center points. - Vector<Point2> outer_points = { - ring_rect.position + Vector2(ring_corner_radius[0], ring_corner_radius[0]), //tl - Point2(ring_rect.position.x + ring_rect.size.x - ring_corner_radius[1], ring_rect.position.y + ring_corner_radius[1]), //tr - ring_rect.position + ring_rect.size - Vector2(ring_corner_radius[2], ring_corner_radius[2]), //br - Point2(ring_rect.position.x + ring_corner_radius[3], ring_rect.position.y + ring_rect.size.y - ring_corner_radius[3]) //bl - }; - - real_t inner_corner_radius[4]; - set_inner_corner_radius(style_rect, inner_rect, corner_radius, inner_corner_radius); - - Vector<Point2> inner_points = { - inner_rect.position + Vector2(inner_corner_radius[0], inner_corner_radius[0]), //tl - Point2(inner_rect.position.x + inner_rect.size.x - inner_corner_radius[1], inner_rect.position.y + inner_corner_radius[1]), //tr - inner_rect.position + inner_rect.size - Vector2(inner_corner_radius[2], inner_corner_radius[2]), //br - Point2(inner_rect.position.x + inner_corner_radius[3], inner_rect.position.y + inner_rect.size.y - inner_corner_radius[3]) //bl - }; - // Calculate the vertices. - - // If the center is filled, we do not draw the border and directly use the inner ring as reference. Because all calls to this - // method either draw a ring or a filled rounded rectangle, but not both. - int max_inner_outer = draw_border ? 2 : 1; - - for (int corner_index = 0; corner_index < 4; corner_index++) { - for (int detail = 0; detail <= adapted_corner_detail; detail++) { - for (int inner_outer = 0; inner_outer < max_inner_outer; inner_outer++) { - real_t radius; - Color color; - Point2 corner_point; - if (inner_outer == 0) { - radius = inner_corner_radius[corner_index]; - color = inner_color; - corner_point = inner_points[corner_index]; - } else { - radius = ring_corner_radius[corner_index]; - color = outer_color; - corner_point = outer_points[corner_index]; - } - - const real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x; - const real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y; - const float x_skew = -skew.x * (y - ring_rect.get_center().y); - const float y_skew = -skew.y * (x - ring_rect.get_center().x); - verts.push_back(Vector2(x + x_skew, y + y_skew)); - colors.push_back(color); - } - } - } - - int ring_vert_count = verts.size() - vert_offset; - - // Fill the indices and the colors for the border. - - if (draw_border) { - for (int i = 0; i < ring_vert_count; i++) { - indices.push_back(vert_offset + ((i + 0) % ring_vert_count)); - indices.push_back(vert_offset + ((i + 2) % ring_vert_count)); - indices.push_back(vert_offset + ((i + 1) % ring_vert_count)); - } - } - - if (is_filled) { - // Compute the triangles pattern to draw the rounded rectangle. - // Consists of vertical stripes of two triangles each. - - int stripes_count = ring_vert_count / 2 - 1; - int last_vert_id = ring_vert_count - 1; - - for (int i = 0; i < stripes_count; i++) { - // Polygon 1. - indices.push_back(vert_offset + i); - indices.push_back(vert_offset + last_vert_id - i - 1); - indices.push_back(vert_offset + i + 1); - // Polygon 2. - indices.push_back(vert_offset + i); - indices.push_back(vert_offset + last_vert_id - 0 - i); - indices.push_back(vert_offset + last_vert_id - 1 - i); - } - } -} - -inline void adapt_values(int p_index_a, int p_index_b, real_t *adapted_values, const real_t *p_values, const real_t p_width, const real_t p_max_a, const real_t p_max_b) { - if (p_values[p_index_a] + p_values[p_index_b] > p_width) { - real_t factor; - real_t new_value; - - factor = (real_t)p_width / (real_t)(p_values[p_index_a] + p_values[p_index_b]); - - new_value = (p_values[p_index_a] * factor); - if (new_value < adapted_values[p_index_a]) { - adapted_values[p_index_a] = new_value; - } - new_value = (p_values[p_index_b] * factor); - if (new_value < adapted_values[p_index_b]) { - adapted_values[p_index_b] = new_value; - } - } else { - adapted_values[p_index_a] = MIN(p_values[p_index_a], adapted_values[p_index_a]); - adapted_values[p_index_b] = MIN(p_values[p_index_b], adapted_values[p_index_b]); - } - adapted_values[p_index_a] = MIN(p_max_a, adapted_values[p_index_a]); - adapted_values[p_index_b] = MIN(p_max_b, adapted_values[p_index_b]); -} - -Rect2 StyleBoxFlat::get_draw_rect(const Rect2 &p_rect) const { - Rect2 draw_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]); - - if (shadow_size > 0) { - Rect2 shadow_rect = draw_rect.grow(shadow_size); - shadow_rect.position += shadow_offset; - draw_rect = draw_rect.merge(shadow_rect); - } - - return draw_rect; -} - -void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { - bool draw_border = (border_width[0] > 0) || (border_width[1] > 0) || (border_width[2] > 0) || (border_width[3] > 0); - bool draw_shadow = (shadow_size > 0); - if (!draw_border && !draw_center && !draw_shadow) { - return; - } - - Rect2 style_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]); - if (Math::is_zero_approx(style_rect.size.width) || Math::is_zero_approx(style_rect.size.height)) { - return; - } - - const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0); - // Only enable antialiasing if it is actually needed. This improve performances - // and maximizes sharpness for non-skewed StyleBoxes with sharp corners. - const bool aa_on = (rounded_corners || !skew.is_zero_approx()) && anti_aliased; - - const bool blend_on = blend_border && draw_border; - - Color border_color_alpha = Color(border_color.r, border_color.g, border_color.b, 0); - Color border_color_blend = (draw_center ? bg_color : border_color_alpha); - Color border_color_inner = blend_on ? border_color_blend : border_color; - - // Adapt borders (prevent weird overlapping/glitchy drawings). - real_t width = MAX(style_rect.size.width, 0); - real_t height = MAX(style_rect.size.height, 0); - real_t adapted_border[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 }; - adapt_values(SIDE_TOP, SIDE_BOTTOM, adapted_border, border_width, height, height, height); - adapt_values(SIDE_LEFT, SIDE_RIGHT, adapted_border, border_width, width, width, width); - - // Adapt corners (prevent weird overlapping/glitchy drawings). - real_t adapted_corner[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 }; - adapt_values(CORNER_TOP_RIGHT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]); - adapt_values(CORNER_TOP_LEFT, CORNER_BOTTOM_LEFT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]); - adapt_values(CORNER_TOP_LEFT, CORNER_TOP_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]); - adapt_values(CORNER_BOTTOM_LEFT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]); - - Rect2 infill_rect = style_rect.grow_individual(-adapted_border[SIDE_LEFT], -adapted_border[SIDE_TOP], -adapted_border[SIDE_RIGHT], -adapted_border[SIDE_BOTTOM]); - - Rect2 border_style_rect = style_rect; - if (aa_on) { - for (int i = 0; i < 4; i++) { - if (border_width[i] > 0) { - border_style_rect = border_style_rect.grow_side((Side)i, -aa_size); - } - } - } - - Vector<Point2> verts; - Vector<int> indices; - Vector<Color> colors; - Vector<Point2> uvs; - - // Create shadow - if (draw_shadow) { - Rect2 shadow_inner_rect = style_rect; - shadow_inner_rect.position += shadow_offset; - - Rect2 shadow_rect = style_rect.grow(shadow_size); - shadow_rect.position += shadow_offset; - - Color shadow_color_transparent = Color(shadow_color.r, shadow_color.g, shadow_color.b, 0); - - draw_rounded_rectangle(verts, indices, colors, shadow_inner_rect, adapted_corner, - shadow_rect, shadow_inner_rect, shadow_color, shadow_color_transparent, corner_detail, skew); - - if (draw_center) { - draw_rounded_rectangle(verts, indices, colors, shadow_inner_rect, adapted_corner, - shadow_inner_rect, shadow_inner_rect, shadow_color, shadow_color, corner_detail, skew, true); - } - } - - // Create border (no AA). - if (draw_border && !aa_on) { - draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, - border_style_rect, infill_rect, border_color_inner, border_color, corner_detail, skew); - } - - // Create infill (no AA). - if (draw_center && (!aa_on || blend_on)) { - draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, - infill_rect, infill_rect, bg_color, bg_color, corner_detail, skew, true); - } - - if (aa_on) { - real_t aa_border_width[4]; - real_t aa_border_width_half[4]; - real_t aa_fill_width[4]; - real_t aa_fill_width_half[4]; - if (draw_border) { - for (int i = 0; i < 4; i++) { - if (border_width[i] > 0) { - aa_border_width[i] = aa_size; - aa_border_width_half[i] = aa_size / 2; - aa_fill_width[i] = 0; - aa_fill_width_half[i] = 0; - } else { - aa_border_width[i] = 0; - aa_border_width_half[i] = 0; - aa_fill_width[i] = aa_size; - aa_fill_width_half[i] = aa_size / 2; - } - } - } else { - for (int i = 0; i < 4; i++) { - aa_border_width[i] = 0; - aa_border_width_half[i] = 0; - aa_fill_width[i] = aa_size; - aa_fill_width_half[i] = aa_size / 2; - } - } - - if (draw_center) { - // Infill rect, transparent side of antialiasing gradient (base infill rect enlarged by AA size) - Rect2 infill_rect_aa_transparent = infill_rect.grow_individual(aa_fill_width_half[SIDE_LEFT], aa_fill_width_half[SIDE_TOP], - aa_fill_width_half[SIDE_RIGHT], aa_fill_width_half[SIDE_BOTTOM]); - // Infill rect, colored side of antialiasing gradient (base infill rect shrunk by AA size) - Rect2 infill_rect_aa_colored = infill_rect_aa_transparent.grow_individual(-aa_fill_width[SIDE_LEFT], -aa_fill_width[SIDE_TOP], - -aa_fill_width[SIDE_RIGHT], -aa_fill_width[SIDE_BOTTOM]); - if (!blend_on) { - // Create center fill, not antialiased yet - draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, - infill_rect_aa_colored, infill_rect_aa_colored, bg_color, bg_color, corner_detail, skew, true); - } - if (!blend_on || !draw_border) { - Color alpha_bg = Color(bg_color.r, bg_color.g, bg_color.b, 0); - // Add antialiasing on the center fill - draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, - infill_rect_aa_transparent, infill_rect_aa_colored, bg_color, alpha_bg, corner_detail, skew); - } - } - - if (draw_border) { - // Inner border recct, fully colored side of antialiasing gradient (base inner rect enlarged by AA size) - Rect2 inner_rect_aa_colored = infill_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP], - aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]); - // Inner border rect, transparent side of antialiasing gradient (base inner rect shrunk by AA size) - Rect2 inner_rect_aa_transparent = inner_rect_aa_colored.grow_individual(-aa_border_width[SIDE_LEFT], -aa_border_width[SIDE_TOP], - -aa_border_width[SIDE_RIGHT], -aa_border_width[SIDE_BOTTOM]); - // Outer border rect, transparent side of antialiasing gradient (base outer rect enlarged by AA size) - Rect2 outer_rect_aa_transparent = style_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP], - aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]); - // Outer border rect, colored side of antialiasing gradient (base outer rect shrunk by AA size) - Rect2 outer_rect_aa_colored = border_style_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP], - aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]); - - // Create border ring, not antialiased yet - draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, - outer_rect_aa_colored, ((blend_on) ? infill_rect : inner_rect_aa_colored), border_color_inner, border_color, corner_detail, skew); - if (!blend_on) { - // Add antialiasing on the ring inner border - draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, - inner_rect_aa_colored, inner_rect_aa_transparent, border_color_blend, border_color, corner_detail, skew); - } - // Add antialiasing on the ring outer border - draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, - outer_rect_aa_transparent, outer_rect_aa_colored, border_color, border_color_alpha, corner_detail, skew); - } - } - - // Compute UV coordinates. - Rect2 uv_rect = style_rect.grow(aa_on ? aa_size : 0); - uvs.resize(verts.size()); - for (int i = 0; i < verts.size(); i++) { - uvs.write[i].x = (verts[i].x - uv_rect.position.x) / uv_rect.size.width; - uvs.write[i].y = (verts[i].y - uv_rect.position.y) / uv_rect.size.height; - } - - // Draw stylebox. - RenderingServer *vs = RenderingServer::get_singleton(); - vs->canvas_item_add_triangle_array(p_canvas_item, indices, verts, colors, uvs); -} - -float StyleBoxFlat::get_style_margin(Side p_side) const { - ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); - return border_width[p_side]; -} - -void StyleBoxFlat::_validate_property(PropertyInfo &p_property) const { - if (!anti_aliased && p_property.name == "anti_aliasing_size") { - p_property.usage = PROPERTY_USAGE_NO_EDITOR; - } -} - -void StyleBoxFlat::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_bg_color", "color"), &StyleBoxFlat::set_bg_color); - ClassDB::bind_method(D_METHOD("get_bg_color"), &StyleBoxFlat::get_bg_color); - - ClassDB::bind_method(D_METHOD("set_border_color", "color"), &StyleBoxFlat::set_border_color); - ClassDB::bind_method(D_METHOD("get_border_color"), &StyleBoxFlat::get_border_color); - - ClassDB::bind_method(D_METHOD("set_border_width_all", "width"), &StyleBoxFlat::set_border_width_all); - ClassDB::bind_method(D_METHOD("get_border_width_min"), &StyleBoxFlat::get_border_width_min); - - ClassDB::bind_method(D_METHOD("set_border_width", "margin", "width"), &StyleBoxFlat::set_border_width); - ClassDB::bind_method(D_METHOD("get_border_width", "margin"), &StyleBoxFlat::get_border_width); - - ClassDB::bind_method(D_METHOD("set_border_blend", "blend"), &StyleBoxFlat::set_border_blend); - ClassDB::bind_method(D_METHOD("get_border_blend"), &StyleBoxFlat::get_border_blend); - - ClassDB::bind_method(D_METHOD("set_corner_radius_all", "radius"), &StyleBoxFlat::set_corner_radius_all); - - ClassDB::bind_method(D_METHOD("set_corner_radius", "corner", "radius"), &StyleBoxFlat::set_corner_radius); - ClassDB::bind_method(D_METHOD("get_corner_radius", "corner"), &StyleBoxFlat::get_corner_radius); - - ClassDB::bind_method(D_METHOD("set_expand_margin", "margin", "size"), &StyleBoxFlat::set_expand_margin); - ClassDB::bind_method(D_METHOD("set_expand_margin_all", "size"), &StyleBoxFlat::set_expand_margin_all); - ClassDB::bind_method(D_METHOD("get_expand_margin", "margin"), &StyleBoxFlat::get_expand_margin); - - ClassDB::bind_method(D_METHOD("set_draw_center", "draw_center"), &StyleBoxFlat::set_draw_center); - ClassDB::bind_method(D_METHOD("is_draw_center_enabled"), &StyleBoxFlat::is_draw_center_enabled); - - ClassDB::bind_method(D_METHOD("set_skew", "skew"), &StyleBoxFlat::set_skew); - ClassDB::bind_method(D_METHOD("get_skew"), &StyleBoxFlat::get_skew); - - ClassDB::bind_method(D_METHOD("set_shadow_color", "color"), &StyleBoxFlat::set_shadow_color); - ClassDB::bind_method(D_METHOD("get_shadow_color"), &StyleBoxFlat::get_shadow_color); - - ClassDB::bind_method(D_METHOD("set_shadow_size", "size"), &StyleBoxFlat::set_shadow_size); - ClassDB::bind_method(D_METHOD("get_shadow_size"), &StyleBoxFlat::get_shadow_size); - - ClassDB::bind_method(D_METHOD("set_shadow_offset", "offset"), &StyleBoxFlat::set_shadow_offset); - ClassDB::bind_method(D_METHOD("get_shadow_offset"), &StyleBoxFlat::get_shadow_offset); - - ClassDB::bind_method(D_METHOD("set_anti_aliased", "anti_aliased"), &StyleBoxFlat::set_anti_aliased); - ClassDB::bind_method(D_METHOD("is_anti_aliased"), &StyleBoxFlat::is_anti_aliased); - - ClassDB::bind_method(D_METHOD("set_aa_size", "size"), &StyleBoxFlat::set_aa_size); - ClassDB::bind_method(D_METHOD("get_aa_size"), &StyleBoxFlat::get_aa_size); - - ClassDB::bind_method(D_METHOD("set_corner_detail", "detail"), &StyleBoxFlat::set_corner_detail); - ClassDB::bind_method(D_METHOD("get_corner_detail"), &StyleBoxFlat::get_corner_detail); - - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "bg_color"), "set_bg_color", "get_bg_color"); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "skew"), "set_skew", "get_skew"); - - ADD_GROUP("Border Width", "border_width_"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_BOTTOM); - - ADD_GROUP("Border", "border_"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color"), "set_border_color", "get_border_color"); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "border_blend"), "set_border_blend", "get_border_blend"); - - ADD_GROUP("Corner Radius", "corner_radius_"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_LEFT); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "corner_detail", PROPERTY_HINT_RANGE, "1,20,1"), "set_corner_detail", "get_corner_detail"); - - ADD_GROUP("Expand Margins", "expand_margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM); - - ADD_GROUP("Shadow", "shadow_"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_shadow_size", "get_shadow_size"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_shadow_offset", "get_shadow_offset"); - - ADD_GROUP("Anti Aliasing", "anti_aliasing_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "anti_aliasing"), "set_anti_aliased", "is_anti_aliased"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "anti_aliasing_size", PROPERTY_HINT_RANGE, "0.01,10,0.001,suffix:px"), "set_aa_size", "get_aa_size"); -} - -StyleBoxFlat::StyleBoxFlat() {} - -StyleBoxFlat::~StyleBoxFlat() {} - -void StyleBoxLine::set_color(const Color &p_color) { - color = p_color; - emit_changed(); -} - -Color StyleBoxLine::get_color() const { - return color; -} - -void StyleBoxLine::set_thickness(int p_thickness) { - thickness = p_thickness; - emit_changed(); -} - -int StyleBoxLine::get_thickness() const { - return thickness; -} - -void StyleBoxLine::set_vertical(bool p_vertical) { - vertical = p_vertical; - emit_changed(); -} - -bool StyleBoxLine::is_vertical() const { - return vertical; -} - -void StyleBoxLine::set_grow_end(float p_grow_end) { - grow_end = p_grow_end; - emit_changed(); -} - -float StyleBoxLine::get_grow_end() const { - return grow_end; -} - -void StyleBoxLine::set_grow_begin(float p_grow_begin) { - grow_begin = p_grow_begin; - emit_changed(); -} - -float StyleBoxLine::get_grow_begin() const { - return grow_begin; -} - -void StyleBoxLine::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_color", "color"), &StyleBoxLine::set_color); - ClassDB::bind_method(D_METHOD("get_color"), &StyleBoxLine::get_color); - ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &StyleBoxLine::set_thickness); - ClassDB::bind_method(D_METHOD("get_thickness"), &StyleBoxLine::get_thickness); - ClassDB::bind_method(D_METHOD("set_grow_begin", "offset"), &StyleBoxLine::set_grow_begin); - ClassDB::bind_method(D_METHOD("get_grow_begin"), &StyleBoxLine::get_grow_begin); - ClassDB::bind_method(D_METHOD("set_grow_end", "offset"), &StyleBoxLine::set_grow_end); - ClassDB::bind_method(D_METHOD("get_grow_end"), &StyleBoxLine::get_grow_end); - ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &StyleBoxLine::set_vertical); - ClassDB::bind_method(D_METHOD("is_vertical"), &StyleBoxLine::is_vertical); - - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_begin", PROPERTY_HINT_RANGE, "-300,300,1,suffix:px"), "set_grow_begin", "get_grow_begin"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_end", PROPERTY_HINT_RANGE, "-300,300,1,suffix:px"), "set_grow_end", "get_grow_end"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,100,suffix:px"), "set_thickness", "get_thickness"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); -} - -float StyleBoxLine::get_style_margin(Side p_side) const { - ERR_FAIL_INDEX_V((int)p_side, 4, 0); - - if (vertical) { - if (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) { - return thickness / 2.0; - } - } else if (p_side == SIDE_TOP || p_side == SIDE_BOTTOM) { - return thickness / 2.0; - } - - return 0; -} - -void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const { - RenderingServer *vs = RenderingServer::get_singleton(); - Rect2i r = p_rect; - - if (vertical) { - r.position.y -= grow_begin; - r.size.y += (grow_begin + grow_end); - r.size.x = thickness; - } else { - r.position.x -= grow_begin; - r.size.x += (grow_begin + grow_end); - r.size.y = thickness; - } - - vs->canvas_item_add_rect(p_canvas_item, r, color); -} - -StyleBoxLine::StyleBoxLine() {} - -StyleBoxLine::~StyleBoxLine() {} diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index 9d96e9a3b7..3f9a96be2f 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -32,8 +32,9 @@ #define STYLE_BOX_H #include "core/io/resource.h" -#include "scene/resources/texture.h" -#include "servers/rendering_server.h" +#include "core/object/class_db.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" class CanvasItem; @@ -41,11 +42,12 @@ class StyleBox : public Resource { GDCLASS(StyleBox, Resource); RES_BASE_EXTENSION("stylebox"); OBJ_SAVE_TYPE(StyleBox); + float content_margin[4]; protected: - virtual float get_style_margin(Side p_side) const { return 0; } static void _bind_methods(); + virtual float get_style_margin(Side p_side) const { return 0; } GDVIRTUAL2C(_draw, RID, Rect2) GDVIRTUAL1RC(Rect2, _get_draw_rect, Rect2) @@ -82,184 +84,4 @@ public: StyleBoxEmpty() {} }; -class StyleBoxTexture : public StyleBox { - GDCLASS(StyleBoxTexture, StyleBox); - -public: - enum AxisStretchMode { - AXIS_STRETCH_MODE_STRETCH, - AXIS_STRETCH_MODE_TILE, - AXIS_STRETCH_MODE_TILE_FIT, - }; - -private: - float expand_margin[4] = {}; - float texture_margin[4] = {}; - Rect2 region_rect; - Ref<Texture2D> texture; - bool draw_center = true; - Color modulate = Color(1, 1, 1, 1); - AxisStretchMode axis_h = AXIS_STRETCH_MODE_STRETCH; - AxisStretchMode axis_v = AXIS_STRETCH_MODE_STRETCH; - -protected: - virtual float get_style_margin(Side p_side) const override; - static void _bind_methods(); - -public: - void set_expand_margin(Side p_expand_side, float p_size); - void set_expand_margin_all(float p_expand_margin_size); - void set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom); - float get_expand_margin(Side p_expand_side) const; - - void set_texture_margin(Side p_side, float p_size); - void set_texture_margin_all(float p_size); - void set_texture_margin_individual(float p_left, float p_top, float p_right, float p_bottom); - float get_texture_margin(Side p_side) const; - - void set_region_rect(const Rect2 &p_region_rect); - Rect2 get_region_rect() const; - - void set_texture(Ref<Texture2D> p_texture); - Ref<Texture2D> get_texture() const; - - void set_draw_center(bool p_enabled); - bool is_draw_center_enabled() const; - - void set_h_axis_stretch_mode(AxisStretchMode p_mode); - AxisStretchMode get_h_axis_stretch_mode() const; - - void set_v_axis_stretch_mode(AxisStretchMode p_mode); - AxisStretchMode get_v_axis_stretch_mode() const; - - void set_modulate(const Color &p_modulate); - Color get_modulate() const; - - virtual Rect2 get_draw_rect(const Rect2 &p_rect) const override; - virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override; - - StyleBoxTexture(); - ~StyleBoxTexture(); -}; - -VARIANT_ENUM_CAST(StyleBoxTexture::AxisStretchMode) - -class StyleBoxFlat : public StyleBox { - GDCLASS(StyleBoxFlat, StyleBox); - - Color bg_color = Color(0.6, 0.6, 0.6); - Color shadow_color = Color(0, 0, 0, 0.6); - Color border_color = Color(0.8, 0.8, 0.8); - - real_t border_width[4] = {}; - real_t expand_margin[4] = {}; - real_t corner_radius[4] = {}; - - bool draw_center = true; - bool blend_border = false; - Vector2 skew; - bool anti_aliased = true; - - int corner_detail = 8; - int shadow_size = 0; - Point2 shadow_offset; - real_t aa_size = 1; - -protected: - virtual float get_style_margin(Side p_side) const override; - static void _bind_methods(); - void _validate_property(PropertyInfo &p_property) const; - -public: - void set_bg_color(const Color &p_color); - Color get_bg_color() const; - - void set_border_color(const Color &p_color); - Color get_border_color() const; - - void set_border_width_all(int p_size); - int get_border_width_min() const; - - void set_border_width(Side p_side, int p_width); - int get_border_width(Side p_side) const; - - void set_border_blend(bool p_blend); - bool get_border_blend() const; - - void set_corner_radius_all(int radius); - void set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_bottom_right, const int radius_bottom_left); - - void set_corner_radius(Corner p_corner, const int radius); - int get_corner_radius(Corner p_corner) const; - - void set_corner_detail(const int &p_corner_detail); - int get_corner_detail() const; - - void set_expand_margin(Side p_expand_side, float p_size); - void set_expand_margin_all(float p_expand_margin_size); - void set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom); - float get_expand_margin(Side p_expand_side) const; - - void set_draw_center(bool p_enabled); - bool is_draw_center_enabled() const; - - void set_skew(Vector2 p_skew); - Vector2 get_skew() const; - - void set_shadow_color(const Color &p_color); - Color get_shadow_color() const; - - void set_shadow_size(const int &p_size); - int get_shadow_size() const; - - void set_shadow_offset(const Point2 &p_offset); - Point2 get_shadow_offset() const; - - void set_anti_aliased(const bool &p_anti_aliased); - bool is_anti_aliased() const; - void set_aa_size(const real_t p_aa_size); - real_t get_aa_size() const; - - virtual Rect2 get_draw_rect(const Rect2 &p_rect) const override; - virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override; - - StyleBoxFlat(); - ~StyleBoxFlat(); -}; - -// Just used to draw lines. -class StyleBoxLine : public StyleBox { - GDCLASS(StyleBoxLine, StyleBox); - Color color; - int thickness = 1; - bool vertical = false; - float grow_begin = 1.0; - float grow_end = 1.0; - -protected: - virtual float get_style_margin(Side p_side) const override; - static void _bind_methods(); - -public: - void set_color(const Color &p_color); - Color get_color() const; - - void set_thickness(int p_thickness); - int get_thickness() const; - - void set_vertical(bool p_vertical); - bool is_vertical() const; - - void set_grow_begin(float p_grow); - float get_grow_begin() const; - - void set_grow_end(float p_grow); - float get_grow_end() const; - - virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override; - - StyleBoxLine(); - ~StyleBoxLine(); -}; - #endif // STYLE_BOX_H diff --git a/scene/resources/style_box_flat.cpp b/scene/resources/style_box_flat.cpp new file mode 100644 index 0000000000..52d02e92cb --- /dev/null +++ b/scene/resources/style_box_flat.cpp @@ -0,0 +1,642 @@ +/**************************************************************************/ +/* style_box_flat.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "style_box_flat.h" + +#include "servers/rendering_server.h" + +float StyleBoxFlat::get_style_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); + return border_width[p_side]; +} + +void StyleBoxFlat::_validate_property(PropertyInfo &p_property) const { + if (!anti_aliased && p_property.name == "anti_aliasing_size") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } +} + +void StyleBoxFlat::set_bg_color(const Color &p_color) { + bg_color = p_color; + emit_changed(); +} + +Color StyleBoxFlat::get_bg_color() const { + return bg_color; +} + +void StyleBoxFlat::set_border_color(const Color &p_color) { + border_color = p_color; + emit_changed(); +} + +Color StyleBoxFlat::get_border_color() const { + return border_color; +} + +void StyleBoxFlat::set_border_width_all(int p_size) { + border_width[0] = p_size; + border_width[1] = p_size; + border_width[2] = p_size; + border_width[3] = p_size; + emit_changed(); +} + +int StyleBoxFlat::get_border_width_min() const { + return MIN(MIN(border_width[0], border_width[1]), MIN(border_width[2], border_width[3])); +} + +void StyleBoxFlat::set_border_width(Side p_side, int p_width) { + ERR_FAIL_INDEX((int)p_side, 4); + border_width[p_side] = p_width; + emit_changed(); +} + +int StyleBoxFlat::get_border_width(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0); + return border_width[p_side]; +} + +void StyleBoxFlat::set_border_blend(bool p_blend) { + blend_border = p_blend; + emit_changed(); +} + +bool StyleBoxFlat::get_border_blend() const { + return blend_border; +} + +void StyleBoxFlat::set_corner_radius(const Corner p_corner, const int radius) { + ERR_FAIL_INDEX((int)p_corner, 4); + corner_radius[p_corner] = radius; + emit_changed(); +} + +void StyleBoxFlat::set_corner_radius_all(int radius) { + for (int i = 0; i < 4; i++) { + corner_radius[i] = radius; + } + + emit_changed(); +} + +void StyleBoxFlat::set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_bottom_right, const int radius_bottom_left) { + corner_radius[0] = radius_top_left; + corner_radius[1] = radius_top_right; + corner_radius[2] = radius_bottom_right; + corner_radius[3] = radius_bottom_left; + + emit_changed(); +} + +int StyleBoxFlat::get_corner_radius(const Corner p_corner) const { + ERR_FAIL_INDEX_V((int)p_corner, 4, 0); + return corner_radius[p_corner]; +} + +void StyleBoxFlat::set_corner_detail(const int &p_corner_detail) { + corner_detail = CLAMP(p_corner_detail, 1, 20); + emit_changed(); +} + +int StyleBoxFlat::get_corner_detail() const { + return corner_detail; +} + +void StyleBoxFlat::set_expand_margin(Side p_side, float p_size) { + ERR_FAIL_INDEX((int)p_side, 4); + expand_margin[p_side] = p_size; + emit_changed(); +} + +void StyleBoxFlat::set_expand_margin_all(float p_expand_margin_size) { + for (int i = 0; i < 4; i++) { + expand_margin[i] = p_expand_margin_size; + } + emit_changed(); +} + +void StyleBoxFlat::set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom) { + expand_margin[SIDE_LEFT] = p_left; + expand_margin[SIDE_TOP] = p_top; + expand_margin[SIDE_RIGHT] = p_right; + expand_margin[SIDE_BOTTOM] = p_bottom; + emit_changed(); +} + +float StyleBoxFlat::get_expand_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); + return expand_margin[p_side]; +} + +void StyleBoxFlat::set_draw_center(bool p_enabled) { + draw_center = p_enabled; + emit_changed(); +} + +bool StyleBoxFlat::is_draw_center_enabled() const { + return draw_center; +} + +void StyleBoxFlat::set_skew(Vector2 p_skew) { + skew = p_skew; + emit_changed(); +} + +Vector2 StyleBoxFlat::get_skew() const { + return skew; +} + +void StyleBoxFlat::set_shadow_color(const Color &p_color) { + shadow_color = p_color; + emit_changed(); +} + +Color StyleBoxFlat::get_shadow_color() const { + return shadow_color; +} + +void StyleBoxFlat::set_shadow_size(const int &p_size) { + shadow_size = p_size; + emit_changed(); +} + +int StyleBoxFlat::get_shadow_size() const { + return shadow_size; +} + +void StyleBoxFlat::set_shadow_offset(const Point2 &p_offset) { + shadow_offset = p_offset; + emit_changed(); +} + +Point2 StyleBoxFlat::get_shadow_offset() const { + return shadow_offset; +} + +void StyleBoxFlat::set_anti_aliased(const bool &p_anti_aliased) { + anti_aliased = p_anti_aliased; + emit_changed(); + notify_property_list_changed(); +} + +bool StyleBoxFlat::is_anti_aliased() const { + return anti_aliased; +} + +void StyleBoxFlat::set_aa_size(const real_t p_aa_size) { + aa_size = CLAMP(p_aa_size, 0.01, 10); + emit_changed(); +} + +real_t StyleBoxFlat::get_aa_size() const { + return aa_size; +} + +inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_rect, const real_t corner_radius[4], real_t *inner_corner_radius) { + real_t border_left = inner_rect.position.x - style_rect.position.x; + real_t border_top = inner_rect.position.y - style_rect.position.y; + real_t border_right = style_rect.size.width - inner_rect.size.width - border_left; + real_t border_bottom = style_rect.size.height - inner_rect.size.height - border_top; + + real_t rad; + + // Top left. + rad = MIN(border_top, border_left); + inner_corner_radius[0] = MAX(corner_radius[0] - rad, 0); + + // Top right; + rad = MIN(border_top, border_right); + inner_corner_radius[1] = MAX(corner_radius[1] - rad, 0); + + // Bottom right. + rad = MIN(border_bottom, border_right); + inner_corner_radius[2] = MAX(corner_radius[2] - rad, 0); + + // Bottom left. + rad = MIN(border_bottom, border_left); + inner_corner_radius[3] = MAX(corner_radius[3] - rad, 0); +} + +inline void draw_rounded_rectangle(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const real_t corner_radius[4], + const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const Vector2 &skew, bool is_filled = false) { + int vert_offset = verts.size(); + if (!vert_offset) { + vert_offset = 0; + } + + int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail; + + bool draw_border = !is_filled; + + real_t ring_corner_radius[4]; + set_inner_corner_radius(style_rect, ring_rect, corner_radius, ring_corner_radius); + + // Corner radius center points. + Vector<Point2> outer_points = { + ring_rect.position + Vector2(ring_corner_radius[0], ring_corner_radius[0]), //tl + Point2(ring_rect.position.x + ring_rect.size.x - ring_corner_radius[1], ring_rect.position.y + ring_corner_radius[1]), //tr + ring_rect.position + ring_rect.size - Vector2(ring_corner_radius[2], ring_corner_radius[2]), //br + Point2(ring_rect.position.x + ring_corner_radius[3], ring_rect.position.y + ring_rect.size.y - ring_corner_radius[3]) //bl + }; + + real_t inner_corner_radius[4]; + set_inner_corner_radius(style_rect, inner_rect, corner_radius, inner_corner_radius); + + Vector<Point2> inner_points = { + inner_rect.position + Vector2(inner_corner_radius[0], inner_corner_radius[0]), //tl + Point2(inner_rect.position.x + inner_rect.size.x - inner_corner_radius[1], inner_rect.position.y + inner_corner_radius[1]), //tr + inner_rect.position + inner_rect.size - Vector2(inner_corner_radius[2], inner_corner_radius[2]), //br + Point2(inner_rect.position.x + inner_corner_radius[3], inner_rect.position.y + inner_rect.size.y - inner_corner_radius[3]) //bl + }; + // Calculate the vertices. + + // If the center is filled, we do not draw the border and directly use the inner ring as reference. Because all calls to this + // method either draw a ring or a filled rounded rectangle, but not both. + int max_inner_outer = draw_border ? 2 : 1; + + for (int corner_index = 0; corner_index < 4; corner_index++) { + for (int detail = 0; detail <= adapted_corner_detail; detail++) { + for (int inner_outer = 0; inner_outer < max_inner_outer; inner_outer++) { + real_t radius; + Color color; + Point2 corner_point; + if (inner_outer == 0) { + radius = inner_corner_radius[corner_index]; + color = inner_color; + corner_point = inner_points[corner_index]; + } else { + radius = ring_corner_radius[corner_index]; + color = outer_color; + corner_point = outer_points[corner_index]; + } + + const real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x; + const real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y; + const float x_skew = -skew.x * (y - ring_rect.get_center().y); + const float y_skew = -skew.y * (x - ring_rect.get_center().x); + verts.push_back(Vector2(x + x_skew, y + y_skew)); + colors.push_back(color); + } + } + } + + int ring_vert_count = verts.size() - vert_offset; + + // Fill the indices and the colors for the border. + + if (draw_border) { + for (int i = 0; i < ring_vert_count; i++) { + indices.push_back(vert_offset + ((i + 0) % ring_vert_count)); + indices.push_back(vert_offset + ((i + 2) % ring_vert_count)); + indices.push_back(vert_offset + ((i + 1) % ring_vert_count)); + } + } + + if (is_filled) { + // Compute the triangles pattern to draw the rounded rectangle. + // Consists of vertical stripes of two triangles each. + + int stripes_count = ring_vert_count / 2 - 1; + int last_vert_id = ring_vert_count - 1; + + for (int i = 0; i < stripes_count; i++) { + // Polygon 1. + indices.push_back(vert_offset + i); + indices.push_back(vert_offset + last_vert_id - i - 1); + indices.push_back(vert_offset + i + 1); + // Polygon 2. + indices.push_back(vert_offset + i); + indices.push_back(vert_offset + last_vert_id - 0 - i); + indices.push_back(vert_offset + last_vert_id - 1 - i); + } + } +} + +inline void adapt_values(int p_index_a, int p_index_b, real_t *adapted_values, const real_t *p_values, const real_t p_width, const real_t p_max_a, const real_t p_max_b) { + if (p_values[p_index_a] + p_values[p_index_b] > p_width) { + real_t factor; + real_t new_value; + + factor = (real_t)p_width / (real_t)(p_values[p_index_a] + p_values[p_index_b]); + + new_value = (p_values[p_index_a] * factor); + if (new_value < adapted_values[p_index_a]) { + adapted_values[p_index_a] = new_value; + } + new_value = (p_values[p_index_b] * factor); + if (new_value < adapted_values[p_index_b]) { + adapted_values[p_index_b] = new_value; + } + } else { + adapted_values[p_index_a] = MIN(p_values[p_index_a], adapted_values[p_index_a]); + adapted_values[p_index_b] = MIN(p_values[p_index_b], adapted_values[p_index_b]); + } + adapted_values[p_index_a] = MIN(p_max_a, adapted_values[p_index_a]); + adapted_values[p_index_b] = MIN(p_max_b, adapted_values[p_index_b]); +} + +Rect2 StyleBoxFlat::get_draw_rect(const Rect2 &p_rect) const { + Rect2 draw_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]); + + if (shadow_size > 0) { + Rect2 shadow_rect = draw_rect.grow(shadow_size); + shadow_rect.position += shadow_offset; + draw_rect = draw_rect.merge(shadow_rect); + } + + return draw_rect; +} + +void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { + bool draw_border = (border_width[0] > 0) || (border_width[1] > 0) || (border_width[2] > 0) || (border_width[3] > 0); + bool draw_shadow = (shadow_size > 0); + if (!draw_border && !draw_center && !draw_shadow) { + return; + } + + Rect2 style_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]); + if (Math::is_zero_approx(style_rect.size.width) || Math::is_zero_approx(style_rect.size.height)) { + return; + } + + const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0); + // Only enable antialiasing if it is actually needed. This improve performances + // and maximizes sharpness for non-skewed StyleBoxes with sharp corners. + const bool aa_on = (rounded_corners || !skew.is_zero_approx()) && anti_aliased; + + const bool blend_on = blend_border && draw_border; + + Color border_color_alpha = Color(border_color.r, border_color.g, border_color.b, 0); + Color border_color_blend = (draw_center ? bg_color : border_color_alpha); + Color border_color_inner = blend_on ? border_color_blend : border_color; + + // Adapt borders (prevent weird overlapping/glitchy drawings). + real_t width = MAX(style_rect.size.width, 0); + real_t height = MAX(style_rect.size.height, 0); + real_t adapted_border[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 }; + adapt_values(SIDE_TOP, SIDE_BOTTOM, adapted_border, border_width, height, height, height); + adapt_values(SIDE_LEFT, SIDE_RIGHT, adapted_border, border_width, width, width, width); + + // Adapt corners (prevent weird overlapping/glitchy drawings). + real_t adapted_corner[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 }; + adapt_values(CORNER_TOP_RIGHT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]); + adapt_values(CORNER_TOP_LEFT, CORNER_BOTTOM_LEFT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]); + adapt_values(CORNER_TOP_LEFT, CORNER_TOP_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]); + adapt_values(CORNER_BOTTOM_LEFT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]); + + Rect2 infill_rect = style_rect.grow_individual(-adapted_border[SIDE_LEFT], -adapted_border[SIDE_TOP], -adapted_border[SIDE_RIGHT], -adapted_border[SIDE_BOTTOM]); + + Rect2 border_style_rect = style_rect; + if (aa_on) { + for (int i = 0; i < 4; i++) { + if (border_width[i] > 0) { + border_style_rect = border_style_rect.grow_side((Side)i, -aa_size); + } + } + } + + Vector<Point2> verts; + Vector<int> indices; + Vector<Color> colors; + Vector<Point2> uvs; + + // Create shadow + if (draw_shadow) { + Rect2 shadow_inner_rect = style_rect; + shadow_inner_rect.position += shadow_offset; + + Rect2 shadow_rect = style_rect.grow(shadow_size); + shadow_rect.position += shadow_offset; + + Color shadow_color_transparent = Color(shadow_color.r, shadow_color.g, shadow_color.b, 0); + + draw_rounded_rectangle(verts, indices, colors, shadow_inner_rect, adapted_corner, + shadow_rect, shadow_inner_rect, shadow_color, shadow_color_transparent, corner_detail, skew); + + if (draw_center) { + draw_rounded_rectangle(verts, indices, colors, shadow_inner_rect, adapted_corner, + shadow_inner_rect, shadow_inner_rect, shadow_color, shadow_color, corner_detail, skew, true); + } + } + + // Create border (no AA). + if (draw_border && !aa_on) { + draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, + border_style_rect, infill_rect, border_color_inner, border_color, corner_detail, skew); + } + + // Create infill (no AA). + if (draw_center && (!aa_on || blend_on)) { + draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, + infill_rect, infill_rect, bg_color, bg_color, corner_detail, skew, true); + } + + if (aa_on) { + real_t aa_border_width[4]; + real_t aa_border_width_half[4]; + real_t aa_fill_width[4]; + real_t aa_fill_width_half[4]; + if (draw_border) { + for (int i = 0; i < 4; i++) { + if (border_width[i] > 0) { + aa_border_width[i] = aa_size; + aa_border_width_half[i] = aa_size / 2; + aa_fill_width[i] = 0; + aa_fill_width_half[i] = 0; + } else { + aa_border_width[i] = 0; + aa_border_width_half[i] = 0; + aa_fill_width[i] = aa_size; + aa_fill_width_half[i] = aa_size / 2; + } + } + } else { + for (int i = 0; i < 4; i++) { + aa_border_width[i] = 0; + aa_border_width_half[i] = 0; + aa_fill_width[i] = aa_size; + aa_fill_width_half[i] = aa_size / 2; + } + } + + if (draw_center) { + // Infill rect, transparent side of antialiasing gradient (base infill rect enlarged by AA size) + Rect2 infill_rect_aa_transparent = infill_rect.grow_individual(aa_fill_width_half[SIDE_LEFT], aa_fill_width_half[SIDE_TOP], + aa_fill_width_half[SIDE_RIGHT], aa_fill_width_half[SIDE_BOTTOM]); + // Infill rect, colored side of antialiasing gradient (base infill rect shrunk by AA size) + Rect2 infill_rect_aa_colored = infill_rect_aa_transparent.grow_individual(-aa_fill_width[SIDE_LEFT], -aa_fill_width[SIDE_TOP], + -aa_fill_width[SIDE_RIGHT], -aa_fill_width[SIDE_BOTTOM]); + if (!blend_on) { + // Create center fill, not antialiased yet + draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, + infill_rect_aa_colored, infill_rect_aa_colored, bg_color, bg_color, corner_detail, skew, true); + } + if (!blend_on || !draw_border) { + Color alpha_bg = Color(bg_color.r, bg_color.g, bg_color.b, 0); + // Add antialiasing on the center fill + draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, + infill_rect_aa_transparent, infill_rect_aa_colored, bg_color, alpha_bg, corner_detail, skew); + } + } + + if (draw_border) { + // Inner border recct, fully colored side of antialiasing gradient (base inner rect enlarged by AA size) + Rect2 inner_rect_aa_colored = infill_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP], + aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]); + // Inner border rect, transparent side of antialiasing gradient (base inner rect shrunk by AA size) + Rect2 inner_rect_aa_transparent = inner_rect_aa_colored.grow_individual(-aa_border_width[SIDE_LEFT], -aa_border_width[SIDE_TOP], + -aa_border_width[SIDE_RIGHT], -aa_border_width[SIDE_BOTTOM]); + // Outer border rect, transparent side of antialiasing gradient (base outer rect enlarged by AA size) + Rect2 outer_rect_aa_transparent = style_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP], + aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]); + // Outer border rect, colored side of antialiasing gradient (base outer rect shrunk by AA size) + Rect2 outer_rect_aa_colored = border_style_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP], + aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]); + + // Create border ring, not antialiased yet + draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, + outer_rect_aa_colored, ((blend_on) ? infill_rect : inner_rect_aa_colored), border_color_inner, border_color, corner_detail, skew); + if (!blend_on) { + // Add antialiasing on the ring inner border + draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, + inner_rect_aa_colored, inner_rect_aa_transparent, border_color_blend, border_color, corner_detail, skew); + } + // Add antialiasing on the ring outer border + draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, + outer_rect_aa_transparent, outer_rect_aa_colored, border_color, border_color_alpha, corner_detail, skew); + } + } + + // Compute UV coordinates. + Rect2 uv_rect = style_rect.grow(aa_on ? aa_size : 0); + uvs.resize(verts.size()); + for (int i = 0; i < verts.size(); i++) { + uvs.write[i].x = (verts[i].x - uv_rect.position.x) / uv_rect.size.width; + uvs.write[i].y = (verts[i].y - uv_rect.position.y) / uv_rect.size.height; + } + + // Draw stylebox. + RenderingServer *vs = RenderingServer::get_singleton(); + vs->canvas_item_add_triangle_array(p_canvas_item, indices, verts, colors, uvs); +} + +void StyleBoxFlat::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_bg_color", "color"), &StyleBoxFlat::set_bg_color); + ClassDB::bind_method(D_METHOD("get_bg_color"), &StyleBoxFlat::get_bg_color); + + ClassDB::bind_method(D_METHOD("set_border_color", "color"), &StyleBoxFlat::set_border_color); + ClassDB::bind_method(D_METHOD("get_border_color"), &StyleBoxFlat::get_border_color); + + ClassDB::bind_method(D_METHOD("set_border_width_all", "width"), &StyleBoxFlat::set_border_width_all); + ClassDB::bind_method(D_METHOD("get_border_width_min"), &StyleBoxFlat::get_border_width_min); + + ClassDB::bind_method(D_METHOD("set_border_width", "margin", "width"), &StyleBoxFlat::set_border_width); + ClassDB::bind_method(D_METHOD("get_border_width", "margin"), &StyleBoxFlat::get_border_width); + + ClassDB::bind_method(D_METHOD("set_border_blend", "blend"), &StyleBoxFlat::set_border_blend); + ClassDB::bind_method(D_METHOD("get_border_blend"), &StyleBoxFlat::get_border_blend); + + ClassDB::bind_method(D_METHOD("set_corner_radius_all", "radius"), &StyleBoxFlat::set_corner_radius_all); + + ClassDB::bind_method(D_METHOD("set_corner_radius", "corner", "radius"), &StyleBoxFlat::set_corner_radius); + ClassDB::bind_method(D_METHOD("get_corner_radius", "corner"), &StyleBoxFlat::get_corner_radius); + + ClassDB::bind_method(D_METHOD("set_expand_margin", "margin", "size"), &StyleBoxFlat::set_expand_margin); + ClassDB::bind_method(D_METHOD("set_expand_margin_all", "size"), &StyleBoxFlat::set_expand_margin_all); + ClassDB::bind_method(D_METHOD("get_expand_margin", "margin"), &StyleBoxFlat::get_expand_margin); + + ClassDB::bind_method(D_METHOD("set_draw_center", "draw_center"), &StyleBoxFlat::set_draw_center); + ClassDB::bind_method(D_METHOD("is_draw_center_enabled"), &StyleBoxFlat::is_draw_center_enabled); + + ClassDB::bind_method(D_METHOD("set_skew", "skew"), &StyleBoxFlat::set_skew); + ClassDB::bind_method(D_METHOD("get_skew"), &StyleBoxFlat::get_skew); + + ClassDB::bind_method(D_METHOD("set_shadow_color", "color"), &StyleBoxFlat::set_shadow_color); + ClassDB::bind_method(D_METHOD("get_shadow_color"), &StyleBoxFlat::get_shadow_color); + + ClassDB::bind_method(D_METHOD("set_shadow_size", "size"), &StyleBoxFlat::set_shadow_size); + ClassDB::bind_method(D_METHOD("get_shadow_size"), &StyleBoxFlat::get_shadow_size); + + ClassDB::bind_method(D_METHOD("set_shadow_offset", "offset"), &StyleBoxFlat::set_shadow_offset); + ClassDB::bind_method(D_METHOD("get_shadow_offset"), &StyleBoxFlat::get_shadow_offset); + + ClassDB::bind_method(D_METHOD("set_anti_aliased", "anti_aliased"), &StyleBoxFlat::set_anti_aliased); + ClassDB::bind_method(D_METHOD("is_anti_aliased"), &StyleBoxFlat::is_anti_aliased); + + ClassDB::bind_method(D_METHOD("set_aa_size", "size"), &StyleBoxFlat::set_aa_size); + ClassDB::bind_method(D_METHOD("get_aa_size"), &StyleBoxFlat::get_aa_size); + + ClassDB::bind_method(D_METHOD("set_corner_detail", "detail"), &StyleBoxFlat::set_corner_detail); + ClassDB::bind_method(D_METHOD("get_corner_detail"), &StyleBoxFlat::get_corner_detail); + + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "bg_color"), "set_bg_color", "get_bg_color"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "skew"), "set_skew", "get_skew"); + + ADD_GROUP("Border Width", "border_width_"); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_BOTTOM); + + ADD_GROUP("Border", "border_"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color"), "set_border_color", "get_border_color"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "border_blend"), "set_border_blend", "get_border_blend"); + + ADD_GROUP("Corner Radius", "corner_radius_"); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_LEFT); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "corner_detail", PROPERTY_HINT_RANGE, "1,20,1"), "set_corner_detail", "get_corner_detail"); + + ADD_GROUP("Expand Margins", "expand_margin_"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM); + + ADD_GROUP("Shadow", "shadow_"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_shadow_size", "get_shadow_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_shadow_offset", "get_shadow_offset"); + + ADD_GROUP("Anti Aliasing", "anti_aliasing_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "anti_aliasing"), "set_anti_aliased", "is_anti_aliased"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "anti_aliasing_size", PROPERTY_HINT_RANGE, "0.01,10,0.001,suffix:px"), "set_aa_size", "get_aa_size"); +} + +StyleBoxFlat::StyleBoxFlat() {} + +StyleBoxFlat::~StyleBoxFlat() {} diff --git a/scene/resources/style_box_flat.h b/scene/resources/style_box_flat.h new file mode 100644 index 0000000000..b6bb145f05 --- /dev/null +++ b/scene/resources/style_box_flat.h @@ -0,0 +1,118 @@ +/**************************************************************************/ +/* style_box_flat.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef STYLE_BOX_FLAT_H +#define STYLE_BOX_FLAT_H + +#include "scene/resources/style_box.h" + +class StyleBoxFlat : public StyleBox { + GDCLASS(StyleBoxFlat, StyleBox); + + Color bg_color = Color(0.6, 0.6, 0.6); + Color shadow_color = Color(0, 0, 0, 0.6); + Color border_color = Color(0.8, 0.8, 0.8); + + real_t border_width[4] = {}; + real_t expand_margin[4] = {}; + real_t corner_radius[4] = {}; + + bool draw_center = true; + bool blend_border = false; + Vector2 skew; + bool anti_aliased = true; + + int corner_detail = 8; + int shadow_size = 0; + Point2 shadow_offset; + real_t aa_size = 1; + +protected: + virtual float get_style_margin(Side p_side) const override; + static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; + +public: + void set_bg_color(const Color &p_color); + Color get_bg_color() const; + + void set_border_color(const Color &p_color); + Color get_border_color() const; + + void set_border_width_all(int p_size); + int get_border_width_min() const; + + void set_border_width(Side p_side, int p_width); + int get_border_width(Side p_side) const; + + void set_border_blend(bool p_blend); + bool get_border_blend() const; + + void set_corner_radius_all(int radius); + void set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_bottom_right, const int radius_bottom_left); + void set_corner_radius(Corner p_corner, const int radius); + int get_corner_radius(Corner p_corner) const; + + void set_corner_detail(const int &p_corner_detail); + int get_corner_detail() const; + + void set_expand_margin(Side p_expand_side, float p_size); + void set_expand_margin_all(float p_expand_margin_size); + void set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom); + float get_expand_margin(Side p_expand_side) const; + + void set_draw_center(bool p_enabled); + bool is_draw_center_enabled() const; + + void set_skew(Vector2 p_skew); + Vector2 get_skew() const; + + void set_shadow_color(const Color &p_color); + Color get_shadow_color() const; + + void set_shadow_size(const int &p_size); + int get_shadow_size() const; + + void set_shadow_offset(const Point2 &p_offset); + Point2 get_shadow_offset() const; + + void set_anti_aliased(const bool &p_anti_aliased); + bool is_anti_aliased() const; + void set_aa_size(const real_t p_aa_size); + real_t get_aa_size() const; + + virtual Rect2 get_draw_rect(const Rect2 &p_rect) const override; + virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override; + + StyleBoxFlat(); + ~StyleBoxFlat(); +}; + +#endif // STYLE_BOX_FLAT_H diff --git a/scene/resources/style_box_line.cpp b/scene/resources/style_box_line.cpp new file mode 100644 index 0000000000..9aeba88531 --- /dev/null +++ b/scene/resources/style_box_line.cpp @@ -0,0 +1,132 @@ +/**************************************************************************/ +/* style_box_line.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "style_box_line.h" + +#include "servers/rendering_server.h" + +float StyleBoxLine::get_style_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0); + + if (vertical) { + if (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) { + return thickness / 2.0; + } + } else if (p_side == SIDE_TOP || p_side == SIDE_BOTTOM) { + return thickness / 2.0; + } + + return 0; +} + +void StyleBoxLine::set_color(const Color &p_color) { + color = p_color; + emit_changed(); +} + +Color StyleBoxLine::get_color() const { + return color; +} + +void StyleBoxLine::set_thickness(int p_thickness) { + thickness = p_thickness; + emit_changed(); +} + +int StyleBoxLine::get_thickness() const { + return thickness; +} + +void StyleBoxLine::set_vertical(bool p_vertical) { + vertical = p_vertical; + emit_changed(); +} + +bool StyleBoxLine::is_vertical() const { + return vertical; +} + +void StyleBoxLine::set_grow_end(float p_grow_end) { + grow_end = p_grow_end; + emit_changed(); +} + +float StyleBoxLine::get_grow_end() const { + return grow_end; +} + +void StyleBoxLine::set_grow_begin(float p_grow_begin) { + grow_begin = p_grow_begin; + emit_changed(); +} + +float StyleBoxLine::get_grow_begin() const { + return grow_begin; +} + +void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const { + RenderingServer *vs = RenderingServer::get_singleton(); + Rect2i r = p_rect; + + if (vertical) { + r.position.y -= grow_begin; + r.size.y += (grow_begin + grow_end); + r.size.x = thickness; + } else { + r.position.x -= grow_begin; + r.size.x += (grow_begin + grow_end); + r.size.y = thickness; + } + + vs->canvas_item_add_rect(p_canvas_item, r, color); +} + +void StyleBoxLine::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_color", "color"), &StyleBoxLine::set_color); + ClassDB::bind_method(D_METHOD("get_color"), &StyleBoxLine::get_color); + ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &StyleBoxLine::set_thickness); + ClassDB::bind_method(D_METHOD("get_thickness"), &StyleBoxLine::get_thickness); + ClassDB::bind_method(D_METHOD("set_grow_begin", "offset"), &StyleBoxLine::set_grow_begin); + ClassDB::bind_method(D_METHOD("get_grow_begin"), &StyleBoxLine::get_grow_begin); + ClassDB::bind_method(D_METHOD("set_grow_end", "offset"), &StyleBoxLine::set_grow_end); + ClassDB::bind_method(D_METHOD("get_grow_end"), &StyleBoxLine::get_grow_end); + ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &StyleBoxLine::set_vertical); + ClassDB::bind_method(D_METHOD("is_vertical"), &StyleBoxLine::is_vertical); + + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_begin", PROPERTY_HINT_RANGE, "-300,300,1,suffix:px"), "set_grow_begin", "get_grow_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_end", PROPERTY_HINT_RANGE, "-300,300,1,suffix:px"), "set_grow_end", "get_grow_end"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,100,suffix:px"), "set_thickness", "get_thickness"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); +} + +StyleBoxLine::StyleBoxLine() {} + +StyleBoxLine::~StyleBoxLine() {} diff --git a/scene/resources/style_box_line.h b/scene/resources/style_box_line.h new file mode 100644 index 0000000000..18f765a1e4 --- /dev/null +++ b/scene/resources/style_box_line.h @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* style_box_line.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef STYLE_BOX_LINE_H +#define STYLE_BOX_LINE_H + +#include "scene/resources/style_box.h" + +class StyleBoxLine : public StyleBox { + GDCLASS(StyleBoxLine, StyleBox); + Color color; + int thickness = 1; + bool vertical = false; + float grow_begin = 1.0; + float grow_end = 1.0; + +protected: + virtual float get_style_margin(Side p_side) const override; + static void _bind_methods(); + +public: + void set_color(const Color &p_color); + Color get_color() const; + + void set_thickness(int p_thickness); + int get_thickness() const; + + void set_vertical(bool p_vertical); + bool is_vertical() const; + + void set_grow_begin(float p_grow); + float get_grow_begin() const; + + void set_grow_end(float p_grow); + float get_grow_end() const; + + virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override; + + StyleBoxLine(); + ~StyleBoxLine(); +}; + +#endif // STYLE_BOX_LINE_H diff --git a/scene/resources/style_box_texture.cpp b/scene/resources/style_box_texture.cpp new file mode 100644 index 0000000000..156525d21c --- /dev/null +++ b/scene/resources/style_box_texture.cpp @@ -0,0 +1,243 @@ +/**************************************************************************/ +/* style_box_texture.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "style_box_texture.h" + +float StyleBoxTexture::get_style_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); + + return texture_margin[p_side]; +} + +void StyleBoxTexture::set_texture(Ref<Texture2D> p_texture) { + if (texture == p_texture) { + return; + } + texture = p_texture; + emit_changed(); +} + +Ref<Texture2D> StyleBoxTexture::get_texture() const { + return texture; +} + +void StyleBoxTexture::set_texture_margin(Side p_side, float p_size) { + ERR_FAIL_INDEX((int)p_side, 4); + + texture_margin[p_side] = p_size; + emit_changed(); +} + +void StyleBoxTexture::set_texture_margin_all(float p_size) { + for (int i = 0; i < 4; i++) { + texture_margin[i] = p_size; + } + emit_changed(); +} + +void StyleBoxTexture::set_texture_margin_individual(float p_left, float p_top, float p_right, float p_bottom) { + texture_margin[SIDE_LEFT] = p_left; + texture_margin[SIDE_TOP] = p_top; + texture_margin[SIDE_RIGHT] = p_right; + texture_margin[SIDE_BOTTOM] = p_bottom; + emit_changed(); +} + +float StyleBoxTexture::get_texture_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); + + return texture_margin[p_side]; +} + +void StyleBoxTexture::set_expand_margin(Side p_side, float p_size) { + ERR_FAIL_INDEX((int)p_side, 4); + expand_margin[p_side] = p_size; + emit_changed(); +} + +void StyleBoxTexture::set_expand_margin_all(float p_expand_margin_size) { + for (int i = 0; i < 4; i++) { + expand_margin[i] = p_expand_margin_size; + } + emit_changed(); +} + +void StyleBoxTexture::set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom) { + expand_margin[SIDE_LEFT] = p_left; + expand_margin[SIDE_TOP] = p_top; + expand_margin[SIDE_RIGHT] = p_right; + expand_margin[SIDE_BOTTOM] = p_bottom; + emit_changed(); +} + +float StyleBoxTexture::get_expand_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0); + return expand_margin[p_side]; +} + +void StyleBoxTexture::set_region_rect(const Rect2 &p_region_rect) { + if (region_rect == p_region_rect) { + return; + } + + region_rect = p_region_rect; + emit_changed(); +} + +Rect2 StyleBoxTexture::get_region_rect() const { + return region_rect; +} + +void StyleBoxTexture::set_draw_center(bool p_enabled) { + draw_center = p_enabled; + emit_changed(); +} + +bool StyleBoxTexture::is_draw_center_enabled() const { + return draw_center; +} + +void StyleBoxTexture::set_h_axis_stretch_mode(AxisStretchMode p_mode) { + ERR_FAIL_INDEX((int)p_mode, 3); + axis_h = p_mode; + emit_changed(); +} + +StyleBoxTexture::AxisStretchMode StyleBoxTexture::get_h_axis_stretch_mode() const { + return axis_h; +} + +void StyleBoxTexture::set_v_axis_stretch_mode(AxisStretchMode p_mode) { + ERR_FAIL_INDEX((int)p_mode, 3); + axis_v = p_mode; + emit_changed(); +} + +StyleBoxTexture::AxisStretchMode StyleBoxTexture::get_v_axis_stretch_mode() const { + return axis_v; +} + +void StyleBoxTexture::set_modulate(const Color &p_modulate) { + if (modulate == p_modulate) { + return; + } + modulate = p_modulate; + emit_changed(); +} + +Color StyleBoxTexture::get_modulate() const { + return modulate; +} + +Rect2 StyleBoxTexture::get_draw_rect(const Rect2 &p_rect) const { + return p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]); +} + +void StyleBoxTexture::draw(RID p_canvas_item, const Rect2 &p_rect) const { + if (texture.is_null()) { + return; + } + + Rect2 rect = p_rect; + Rect2 src_rect = region_rect; + + texture->get_rect_region(rect, src_rect, rect, src_rect); + + rect.position.x -= expand_margin[SIDE_LEFT]; + rect.position.y -= expand_margin[SIDE_TOP]; + rect.size.x += expand_margin[SIDE_LEFT] + expand_margin[SIDE_RIGHT]; + rect.size.y += expand_margin[SIDE_TOP] + expand_margin[SIDE_BOTTOM]; + + Vector2 start_offset = Vector2(texture_margin[SIDE_LEFT], texture_margin[SIDE_TOP]); + Vector2 end_offset = Vector2(texture_margin[SIDE_RIGHT], texture_margin[SIDE_BOTTOM]); + + RenderingServer::get_singleton()->canvas_item_add_nine_patch(p_canvas_item, rect, src_rect, texture->get_rid(), start_offset, end_offset, RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center, modulate); +} + +void StyleBoxTexture::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_texture", "texture"), &StyleBoxTexture::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &StyleBoxTexture::get_texture); + + ClassDB::bind_method(D_METHOD("set_texture_margin", "margin", "size"), &StyleBoxTexture::set_texture_margin); + ClassDB::bind_method(D_METHOD("set_texture_margin_all", "size"), &StyleBoxTexture::set_texture_margin_all); + ClassDB::bind_method(D_METHOD("get_texture_margin", "margin"), &StyleBoxTexture::get_texture_margin); + + ClassDB::bind_method(D_METHOD("set_expand_margin", "margin", "size"), &StyleBoxTexture::set_expand_margin); + ClassDB::bind_method(D_METHOD("set_expand_margin_all", "size"), &StyleBoxTexture::set_expand_margin_all); + ClassDB::bind_method(D_METHOD("get_expand_margin", "margin"), &StyleBoxTexture::get_expand_margin); + + ClassDB::bind_method(D_METHOD("set_region_rect", "region"), &StyleBoxTexture::set_region_rect); + ClassDB::bind_method(D_METHOD("get_region_rect"), &StyleBoxTexture::get_region_rect); + + ClassDB::bind_method(D_METHOD("set_draw_center", "enable"), &StyleBoxTexture::set_draw_center); + ClassDB::bind_method(D_METHOD("is_draw_center_enabled"), &StyleBoxTexture::is_draw_center_enabled); + + ClassDB::bind_method(D_METHOD("set_modulate", "color"), &StyleBoxTexture::set_modulate); + ClassDB::bind_method(D_METHOD("get_modulate"), &StyleBoxTexture::get_modulate); + + ClassDB::bind_method(D_METHOD("set_h_axis_stretch_mode", "mode"), &StyleBoxTexture::set_h_axis_stretch_mode); + ClassDB::bind_method(D_METHOD("get_h_axis_stretch_mode"), &StyleBoxTexture::get_h_axis_stretch_mode); + + ClassDB::bind_method(D_METHOD("set_v_axis_stretch_mode", "mode"), &StyleBoxTexture::set_v_axis_stretch_mode); + ClassDB::bind_method(D_METHOD("get_v_axis_stretch_mode"), &StyleBoxTexture::get_v_axis_stretch_mode); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); + + ADD_GROUP("Texture Margins", "texture_margin_"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_BOTTOM); + + ADD_GROUP("Expand Margins", "expand_margin_"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM); + + ADD_GROUP("Axis Stretch", "axis_stretch_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode"); + + ADD_GROUP("Sub-Region", "region_"); + ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect", PROPERTY_HINT_NONE, "suffix:px"), "set_region_rect", "get_region_rect"); + + ADD_GROUP("Modulate", "modulate_"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate_color"), "set_modulate", "get_modulate"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled"); + + BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_STRETCH); + BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_TILE); + BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_TILE_FIT); +} + +StyleBoxTexture::StyleBoxTexture() {} + +StyleBoxTexture::~StyleBoxTexture() {} diff --git a/scene/resources/style_box_texture.h b/scene/resources/style_box_texture.h new file mode 100644 index 0000000000..b1b833f470 --- /dev/null +++ b/scene/resources/style_box_texture.h @@ -0,0 +1,99 @@ +/**************************************************************************/ +/* style_box_texture.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef STYLE_BOX_TEXTURE_H +#define STYLE_BOX_TEXTURE_H + +#include "scene/resources/style_box.h" +#include "scene/resources/texture.h" + +class StyleBoxTexture : public StyleBox { + GDCLASS(StyleBoxTexture, StyleBox); + +public: + enum AxisStretchMode { + AXIS_STRETCH_MODE_STRETCH, + AXIS_STRETCH_MODE_TILE, + AXIS_STRETCH_MODE_TILE_FIT, + }; + +private: + float expand_margin[4] = {}; + float texture_margin[4] = {}; + Rect2 region_rect; + Ref<Texture2D> texture; + bool draw_center = true; + Color modulate = Color(1, 1, 1, 1); + AxisStretchMode axis_h = AXIS_STRETCH_MODE_STRETCH; + AxisStretchMode axis_v = AXIS_STRETCH_MODE_STRETCH; + +protected: + virtual float get_style_margin(Side p_side) const override; + static void _bind_methods(); + +public: + void set_texture(Ref<Texture2D> p_texture); + Ref<Texture2D> get_texture() const; + + void set_texture_margin(Side p_side, float p_size); + void set_texture_margin_all(float p_size); + void set_texture_margin_individual(float p_left, float p_top, float p_right, float p_bottom); + float get_texture_margin(Side p_side) const; + + void set_expand_margin(Side p_expand_side, float p_size); + void set_expand_margin_all(float p_expand_margin_size); + void set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom); + float get_expand_margin(Side p_expand_side) const; + + void set_region_rect(const Rect2 &p_region_rect); + Rect2 get_region_rect() const; + + void set_draw_center(bool p_enabled); + bool is_draw_center_enabled() const; + + void set_h_axis_stretch_mode(AxisStretchMode p_mode); + AxisStretchMode get_h_axis_stretch_mode() const; + + void set_v_axis_stretch_mode(AxisStretchMode p_mode); + AxisStretchMode get_v_axis_stretch_mode() const; + + void set_modulate(const Color &p_modulate); + Color get_modulate() const; + + virtual Rect2 get_draw_rect(const Rect2 &p_rect) const override; + virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override; + + StyleBoxTexture(); + ~StyleBoxTexture(); +}; + +VARIANT_ENUM_CAST(StyleBoxTexture::AxisStretchMode) + +#endif // STYLE_BOX_TEXTURE_H diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index bcbc8b94e7..799a8471b9 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -222,13 +222,13 @@ void Theme::set_default_font(const Ref<Font> &p_default_font) { } if (default_font.is_valid()) { - default_font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + default_font->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } default_font = p_default_font; if (default_font.is_valid()) { - default_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED); + default_font->connect_changed(callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(); @@ -268,13 +268,13 @@ void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, c bool existing = false; if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) { existing = true; - icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + icon_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } icon_map[p_theme_type][p_name] = p_icon; if (p_icon.is_valid()) { - icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED); + icon_map[p_theme_type][p_name]->connect_changed(callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(!existing); @@ -314,7 +314,7 @@ void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_name), "Cannot clear the icon '" + String(p_name) + "' because it does not exist."); if (icon_map[p_theme_type][p_name].is_valid()) { - icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + icon_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } icon_map[p_theme_type].erase(p_name); @@ -353,7 +353,7 @@ void Theme::remove_icon_type(const StringName &p_theme_type) { for (const KeyValue<StringName, Ref<Texture2D>> &E : icon_map[p_theme_type]) { Ref<Texture2D> icon = E.value; if (icon.is_valid()) { - icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + icon->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } } @@ -378,13 +378,13 @@ void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_typ bool existing = false; if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) { existing = true; - style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + style_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } style_map[p_theme_type][p_name] = p_style; if (p_style.is_valid()) { - style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED); + style_map[p_theme_type][p_name]->connect_changed(callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(!existing); @@ -424,7 +424,7 @@ void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_t ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_name), "Cannot clear the stylebox '" + String(p_name) + "' because it does not exist."); if (style_map[p_theme_type][p_name].is_valid()) { - style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + style_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } style_map[p_theme_type].erase(p_name); @@ -463,7 +463,7 @@ void Theme::remove_stylebox_type(const StringName &p_theme_type) { for (const KeyValue<StringName, Ref<StyleBox>> &E : style_map[p_theme_type]) { Ref<StyleBox> style = E.value; if (style.is_valid()) { - style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + style->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } } @@ -488,13 +488,13 @@ void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, c bool existing = false; if (font_map[p_theme_type][p_name].is_valid()) { existing = true; - font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + font_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } font_map[p_theme_type][p_name] = p_font; if (p_font.is_valid()) { - font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED); + font_map[p_theme_type][p_name]->connect_changed(callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(!existing); @@ -536,7 +536,7 @@ void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_name), "Cannot clear the font '" + String(p_name) + "' because it does not exist."); if (font_map[p_theme_type][p_name].is_valid()) { - font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + font_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } font_map[p_theme_type].erase(p_name); @@ -575,7 +575,7 @@ void Theme::remove_font_type(const StringName &p_theme_type) { for (const KeyValue<StringName, Ref<Font>> &E : font_map[p_theme_type]) { Ref<Font> font = E.value; if (font.is_valid()) { - font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + font->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } } @@ -1622,7 +1622,7 @@ void Theme::clear() { for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) { if (F.value.is_valid()) { Ref<Texture2D> icon = F.value; - icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + icon->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } } } @@ -1633,7 +1633,7 @@ void Theme::clear() { for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) { if (F.value.is_valid()) { Ref<StyleBox> style = F.value; - style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + style->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } } } @@ -1644,7 +1644,7 @@ void Theme::clear() { for (const KeyValue<StringName, Ref<Font>> &F : E.value) { if (F.value.is_valid()) { Ref<Font> font = F.value; - font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + font->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed)); } } } diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 0331f3af0a..19f57dda00 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -30,7 +30,6 @@ #include "tile_set.h" -#include "core/core_string_names.h" #include "core/io/marshalls.h" #include "core/math/geometry_2d.h" #include "core/templates/local_vector.h" @@ -488,7 +487,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source p_tile_set_source->set_tile_set(this); _compute_next_source_id(); - sources[new_source_id]->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed)); + sources[new_source_id]->connect_changed(callable_mp(this, &TileSet::_source_changed)); terrains_cache_dirty = true; emit_changed(); @@ -499,7 +498,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source void TileSet::remove_source(int p_source_id) { ERR_FAIL_COND_MSG(!sources.has(p_source_id), vformat("Cannot remove TileSet atlas source. No tileset atlas source with id %d.", p_source_id)); - sources[p_source_id]->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed)); + sources[p_source_id]->disconnect_changed(callable_mp(this, &TileSet::_source_changed)); sources[p_source_id]->set_tile_set(nullptr); sources.erase(p_source_id); @@ -3815,13 +3814,13 @@ void TileSetAtlasSource::reset_state() { void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) { if (texture.is_valid()) { - texture->disconnect(SNAME("changed"), callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture)); + texture->disconnect_changed(callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture)); } texture = p_texture; if (texture.is_valid()) { - texture->connect(SNAME("changed"), callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture)); + texture->connect_changed(callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture)); } _clear_tiles_outside_texture(); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 0c2c21380a..8d034d3458 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -767,7 +767,7 @@ void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, co input->shader_type = p_type; } - n.node->connect("changed", callable_mp(this, &VisualShader::_queue_update)); + n.node->connect_changed(callable_mp(this, &VisualShader::_queue_update)); Ref<VisualShaderNodeCustom> custom = n.node; if (custom.is_valid()) { @@ -834,7 +834,7 @@ void VisualShader::remove_node(Type p_type, int p_id) { Graph *g = &graph[p_type]; ERR_FAIL_COND(!g->nodes.has(p_id)); - g->nodes[p_id].node->disconnect("changed", callable_mp(this, &VisualShader::_queue_update)); + g->nodes[p_id].node->disconnect_changed(callable_mp(this, &VisualShader::_queue_update)); g->nodes.erase(p_id); @@ -907,7 +907,7 @@ void VisualShader::replace_node(Type p_type, int p_id, const StringName &p_new_c } } - vsn->connect("changed", callable_mp(this, &VisualShader::_queue_update)); + vsn->connect_changed(callable_mp(this, &VisualShader::_queue_update)); g->nodes[p_id].node = Ref<VisualShaderNode>(vsn); _queue_update(); diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp index 40b8fd4b64..cfea6e21ee 100644 --- a/scene/resources/visual_shader_particle_nodes.cpp +++ b/scene/resources/visual_shader_particle_nodes.cpp @@ -30,7 +30,6 @@ #include "visual_shader_particle_nodes.h" -#include "core/core_string_names.h" #include "scene/resources/image_texture.h" // VisualShaderNodeParticleEmitter @@ -638,21 +637,13 @@ void VisualShaderNodeParticleMeshEmitter::set_mesh(Ref<Mesh> p_mesh) { } if (mesh.is_valid()) { - Callable callable = callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures); - - if (mesh->is_connected(CoreStringNames::get_singleton()->changed, callable)) { - mesh->disconnect(CoreStringNames::get_singleton()->changed, callable); - } + mesh->disconnect_changed(callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures)); } mesh = p_mesh; if (mesh.is_valid()) { - Callable callable = callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures); - - if (!mesh->is_connected(CoreStringNames::get_singleton()->changed, callable)) { - mesh->connect(CoreStringNames::get_singleton()->changed, callable); - } + mesh->connect_changed(callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures)); } emit_changed(); @@ -733,7 +724,7 @@ void VisualShaderNodeParticleMeshEmitter::_bind_methods() { } VisualShaderNodeParticleMeshEmitter::VisualShaderNodeParticleMeshEmitter() { - connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures)); + connect_changed(callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures)); position_texture.instantiate(); normal_texture.instantiate(); diff --git a/scene/resources/world_boundary_shape_3d.cpp b/scene/resources/world_boundary_shape_3d.cpp index 3074bd1fd8..beaaddc95e 100644 --- a/scene/resources/world_boundary_shape_3d.cpp +++ b/scene/resources/world_boundary_shape_3d.cpp @@ -69,7 +69,7 @@ void WorldBoundaryShape3D::_update_shape() { void WorldBoundaryShape3D::set_plane(const Plane &p_plane) { plane = p_plane; _update_shape(); - notify_change_to_owners(); + emit_changed(); } const Plane &WorldBoundaryShape3D::get_plane() const { diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index 3b26945c86..4da479639d 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -185,10 +185,10 @@ NavigationServer3D::NavigationServer3D() { ERR_FAIL_COND(singleton != nullptr); singleton = this; - GLOBAL_DEF_BASIC("navigation/2d/default_cell_size", 1); + GLOBAL_DEF_BASIC("navigation/2d/default_cell_size", 1.0); GLOBAL_DEF("navigation/2d/use_edge_connections", true); - GLOBAL_DEF_BASIC("navigation/2d/default_edge_connection_margin", 1); - GLOBAL_DEF_BASIC("navigation/2d/default_link_connection_radius", 4); + GLOBAL_DEF_BASIC("navigation/2d/default_edge_connection_margin", 1.0); + GLOBAL_DEF_BASIC("navigation/2d/default_link_connection_radius", 4.0); GLOBAL_DEF_BASIC("navigation/3d/default_cell_size", 0.25); GLOBAL_DEF_BASIC("navigation/3d/default_cell_height", 0.25); diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp index 86484c982a..eba1c145e3 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.cpp +++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp @@ -962,7 +962,7 @@ void CopyEffects::set_color_raster(RID p_dest_texture, const Color &p_color, con RD::get_singleton()->draw_list_end(); } -void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip) { +void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip, BitField<RD::BarrierMask> p_post_barrier) { UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -994,7 +994,7 @@ void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuf RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(CopyToDPPushConstant)); RD::get_singleton()->draw_list_draw(draw_list, true); - RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_TRANSFER); + RD::get_singleton()->draw_list_end(p_post_barrier); } void CopyEffects::cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size) { diff --git a/servers/rendering/renderer_rd/effects/copy_effects.h b/servers/rendering/renderer_rd/effects/copy_effects.h index 3cd26d0f7e..470ac1acee 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.h +++ b/servers/rendering/renderer_rd/effects/copy_effects.h @@ -341,7 +341,7 @@ public: void set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst = false); void set_color_raster(RID p_dest_texture, const Color &p_color, const Rect2i &p_region); - void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip); + void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip, BitField<RD::BarrierMask> p_post_barrier = RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_TRANSFER); void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size); void cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, const Size2i &p_size); void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array); diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 618348c688..c29204ed1e 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -1245,14 +1245,15 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info); if (finalize_cubemap) { _render_shadow_process(); - _render_shadow_end(); - //reblit + _render_shadow_end(RD::BARRIER_MASK_FRAGMENT); + + // reblit Rect2 atlas_rect_norm = atlas_rect; atlas_rect_norm.position /= float(atlas_size); atlas_rect_norm.size /= float(atlas_size); - copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), false); + copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), false, RD::BARRIER_MASK_NO_BARRIER); atlas_rect_norm.position += Vector2(dual_paraboloid_offset) * atlas_rect_norm.size; - copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), true); + copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), true, RD::BARRIER_MASK_NO_BARRIER); //restore transform so it can be properly used light_storage->light_instance_set_shadow_transform(p_light, Projection(), light_storage->light_instance_get_base_transform(p_light), zfar, 0, 0, 0); @@ -1362,7 +1363,7 @@ void RenderForwardMobile::_render_shadow_end(uint32_t p_barrier) { } if (p_barrier != RD::BARRIER_MASK_NO_BARRIER) { - RD::get_singleton()->barrier(RD::BARRIER_MASK_RASTER, p_barrier); + RD::get_singleton()->barrier(RD::BARRIER_MASK_FRAGMENT, p_barrier); } RD::get_singleton()->draw_command_end_label(); } diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index d87c09a857..2dd11e5efe 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -1073,9 +1073,11 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM); BIND_ENUM_CONSTANT(DATA_FORMAT_MAX); - BIND_BITFIELD_FLAG(BARRIER_MASK_RASTER); + BIND_BITFIELD_FLAG(BARRIER_MASK_VERTEX); + BIND_BITFIELD_FLAG(BARRIER_MASK_FRAGMENT); BIND_BITFIELD_FLAG(BARRIER_MASK_COMPUTE); BIND_BITFIELD_FLAG(BARRIER_MASK_TRANSFER); + BIND_BITFIELD_FLAG(BARRIER_MASK_RASTER); BIND_BITFIELD_FLAG(BARRIER_MASK_ALL_BARRIERS); BIND_BITFIELD_FLAG(BARRIER_MASK_NO_BARRIER); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index dd2f8d55aa..0a362962a1 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -393,11 +393,14 @@ public: /*****************/ enum BarrierMask { - BARRIER_MASK_RASTER = 1, - BARRIER_MASK_COMPUTE = 2, - BARRIER_MASK_TRANSFER = 4, + BARRIER_MASK_VERTEX = 1, + BARRIER_MASK_FRAGMENT = 2, + BARRIER_MASK_COMPUTE = 4, + BARRIER_MASK_TRANSFER = 8, + + BARRIER_MASK_RASTER = BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT, // 3, BARRIER_MASK_ALL_BARRIERS = BARRIER_MASK_RASTER | BARRIER_MASK_COMPUTE | BARRIER_MASK_TRANSFER, // 7 - BARRIER_MASK_NO_BARRIER = 8, + BARRIER_MASK_NO_BARRIER = 16, }; /*****************/ diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index ff4f670c58..04292e1f8c 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -10549,19 +10549,23 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ } } else if ((int(completion_base) > int(TYPE_MAT4) && int(completion_base) < int(TYPE_STRUCT))) { Vector<String> options; + if (current_uniform_filter == FILTER_DEFAULT) { + options.push_back("filter_linear"); + options.push_back("filter_linear_mipmap"); + options.push_back("filter_linear_mipmap_anisotropic"); + options.push_back("filter_nearest"); + options.push_back("filter_nearest_mipmap"); + options.push_back("filter_nearest_mipmap_anisotropic"); + } + if (current_uniform_repeat == REPEAT_DEFAULT) { + options.push_back("repeat_enable"); + options.push_back("repeat_disable"); + } if (completion_base_array) { if (current_uniform_hint == ShaderNode::Uniform::HINT_NONE) { options.push_back("source_color"); } } else { - if (current_uniform_filter == FILTER_DEFAULT) { - options.push_back("filter_linear"); - options.push_back("filter_linear_mipmap"); - options.push_back("filter_linear_mipmap_anisotropic"); - options.push_back("filter_nearest"); - options.push_back("filter_nearest_mipmap"); - options.push_back("filter_nearest_mipmap_anisotropic"); - } if (current_uniform_hint == ShaderNode::Uniform::HINT_NONE) { options.push_back("hint_anisotropy"); options.push_back("hint_default_black"); @@ -10579,10 +10583,6 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ options.push_back("hint_depth_texture"); options.push_back("source_color"); } - if (current_uniform_repeat == REPEAT_DEFAULT) { - options.push_back("repeat_enable"); - options.push_back("repeat_disable"); - } } for (int i = 0; i < options.size(); i++) { diff --git a/tests/core/variant/test_array.h b/tests/core/variant/test_array.h index ccb02ed5fa..228d77b3b5 100644 --- a/tests/core/variant/test_array.h +++ b/tests/core/variant/test_array.h @@ -304,13 +304,31 @@ TEST_CASE("[Array] slice()") { CHECK(slice8[1] == Variant(3)); CHECK(slice8[2] == Variant(1)); + Array slice9 = array.slice(10, 0, -2); + CHECK(slice9.size() == 3); + CHECK(slice9[0] == Variant(5)); + CHECK(slice9[1] == Variant(3)); + CHECK(slice9[2] == Variant(1)); + + Array slice10 = array.slice(2, -10, -1); + CHECK(slice10.size() == 3); + CHECK(slice10[0] == Variant(2)); + CHECK(slice10[1] == Variant(1)); + CHECK(slice10[2] == Variant(0)); + ERR_PRINT_OFF; - Array slice9 = array.slice(4, 1); - CHECK(slice9.size() == 0); + Array slice11 = array.slice(4, 1); + CHECK(slice11.size() == 0); - Array slice10 = array.slice(3, -4); - CHECK(slice10.size() == 0); + Array slice12 = array.slice(3, -4); + CHECK(slice12.size() == 0); ERR_PRINT_ON; + + Array slice13 = Array().slice(1); + CHECK(slice13.size() == 0); + + Array slice14 = array.slice(6); + CHECK(slice14.size() == 0); } TEST_CASE("[Array] Duplicate array") { diff --git a/tests/scene/test_theme.h b/tests/scene/test_theme.h index 59a5ad2cff..ad1ce1fd50 100644 --- a/tests/scene/test_theme.h +++ b/tests/scene/test_theme.h @@ -32,6 +32,7 @@ #define TEST_THEME_H #include "scene/resources/image_texture.h" +#include "scene/resources/style_box_flat.h" #include "scene/resources/theme.h" #include "tests/test_tools.h" diff --git a/thirdparty/README.md b/thirdparty/README.md index 071dea330c..a918acbe77 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -115,7 +115,7 @@ commits. ## enet - Upstream: http://enet.bespin.org -- Version: 1.3.17 (e0e7045b7e056b454b5093cb34df49dc4cee0bee, 2020) +- Version: git (ea4607a90dbfbcf4da2669ea998585253d8e70b1, 2023) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/enet/enet/enet.h b/thirdparty/enet/enet/enet.h index 77f8004b80..5232f8a869 100644 --- a/thirdparty/enet/enet/enet.h +++ b/thirdparty/enet/enet/enet.h @@ -68,7 +68,8 @@ typedef enum _ENetSocketOption ENET_SOCKOPT_RCVTIMEO = 6, ENET_SOCKOPT_SNDTIMEO = 7, ENET_SOCKOPT_ERROR = 8, - ENET_SOCKOPT_NODELAY = 9 + ENET_SOCKOPT_NODELAY = 9, + ENET_SOCKOPT_TTL = 10 } ENetSocketOption; typedef enum _ENetSocketShutdown @@ -179,7 +180,7 @@ typedef struct _ENetOutgoingCommand enet_uint16 unreliableSequenceNumber; enet_uint32 sentTime; enet_uint32 roundTripTimeout; - enet_uint32 roundTripTimeoutLimit; + enet_uint32 queueTime; enet_uint32 fragmentOffset; enet_uint16 fragmentLength; enet_uint16 sendAttempts; @@ -222,7 +223,7 @@ enum ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024, ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024, ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000, - ENET_HOST_DEFAULT_MTU = 1400, + ENET_HOST_DEFAULT_MTU = 1392, ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024, ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024, @@ -262,7 +263,8 @@ typedef struct _ENetChannel typedef enum _ENetPeerFlag { - ENET_PEER_FLAG_NEEDS_DISPATCH = (1 << 0) + ENET_PEER_FLAG_NEEDS_DISPATCH = (1 << 0), + ENET_PEER_FLAG_CONTINUE_SENDING = (1 << 1) } ENetPeerFlag; /** @@ -322,7 +324,7 @@ typedef struct _ENetPeer enet_uint16 outgoingReliableSequenceNumber; ENetList acknowledgements; ENetList sentReliableCommands; - ENetList sentUnreliableCommands; + ENetList outgoingSendReliableCommands; ENetList outgoingCommands; ENetList dispatchedCommands; enet_uint16 flags; @@ -385,7 +387,7 @@ typedef struct _ENetHost size_t channelLimit; /**< maximum number of channels allowed for connected peers */ enet_uint32 serviceTime; ENetList dispatchQueue; - int continueSending; + enet_uint32 totalQueued; size_t packetSize; enet_uint16 headerFlags; ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS]; @@ -585,6 +587,7 @@ ENET_API void enet_host_channel_limit (ENetHost *, size_t); ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32); extern void enet_host_bandwidth_throttle (ENetHost *); extern enet_uint32 enet_host_random_seed (void); +extern enet_uint32 enet_host_random (ENetHost *); ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID); @@ -598,6 +601,7 @@ ENET_API void enet_peer_disconnect_later (ENetPeer *, enet_uint32 ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32); extern int enet_peer_throttle (ENetPeer *, enet_uint32); extern void enet_peer_reset_queues (ENetPeer *); +extern int enet_peer_has_outgoing_commands (ENetPeer *); extern void enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *); extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16); extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32); diff --git a/thirdparty/enet/godot.cpp b/thirdparty/enet/godot.cpp index 2cbfe59fc6..9ce126a475 100644 --- a/thirdparty/enet/godot.cpp +++ b/thirdparty/enet/godot.cpp @@ -535,6 +535,10 @@ int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buf if (err == ERR_BUSY) { return 0; } + if (err == ERR_OUT_OF_MEMORY) { + // A packet above the ENET_PROTOCOL_MAXIMUM_MTU was received. + return -2; + } if (err != OK) { return -1; diff --git a/thirdparty/enet/host.c b/thirdparty/enet/host.c index 21ab27e247..adb3533cf1 100644 --- a/thirdparty/enet/host.c +++ b/thirdparty/enet/host.c @@ -96,6 +96,7 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL host -> totalSentPackets = 0; host -> totalReceivedData = 0; host -> totalReceivedPackets = 0; + host -> totalQueued = 0; host -> connectedPeers = 0; host -> bandwidthLimitedPeers = 0; @@ -123,8 +124,8 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL enet_list_clear (& currentPeer -> acknowledgements); enet_list_clear (& currentPeer -> sentReliableCommands); - enet_list_clear (& currentPeer -> sentUnreliableCommands); enet_list_clear (& currentPeer -> outgoingCommands); + enet_list_clear (& currentPeer -> outgoingSendReliableCommands); enet_list_clear (& currentPeer -> dispatchedCommands); enet_peer_reset (currentPeer); @@ -160,6 +161,16 @@ enet_host_destroy (ENetHost * host) enet_free (host); } +enet_uint32 +enet_host_random (ENetHost * host) +{ + /* Mulberry32 by Tommy Ettinger */ + enet_uint32 n = (host -> randomSeed += 0x6D2B79F5U); + n = (n ^ (n >> 15)) * (n | 1U); + n ^= n + (n ^ (n >> 7)) * (n | 61U); + return n ^ (n >> 14); +} + /** Initiates a connection to a foreign host. @param host host seeking the connection @param address destination for the connection @@ -199,7 +210,8 @@ enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelC currentPeer -> channelCount = channelCount; currentPeer -> state = ENET_PEER_STATE_CONNECTING; currentPeer -> address = * address; - currentPeer -> connectID = ++ host -> randomSeed; + currentPeer -> connectID = enet_host_random (host); + currentPeer -> mtu = host -> mtu; if (host -> outgoingBandwidth == 0) currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; diff --git a/thirdparty/enet/packet.c b/thirdparty/enet/packet.c index 5fa78b28ae..d51c640404 100644 --- a/thirdparty/enet/packet.c +++ b/thirdparty/enet/packet.c @@ -98,53 +98,46 @@ enet_packet_resize (ENetPacket * packet, size_t dataLength) return 0; } -static int initializedCRC32 = 0; -static enet_uint32 crcTable [256]; - -static enet_uint32 -reflect_crc (int val, int bits) +static const enet_uint32 crcTable [256] = { - int result = 0, bit; - - for (bit = 0; bit < bits; bit ++) - { - if(val & 1) result |= 1 << (bits - 1 - bit); - val >>= 1; - } - - return result; -} - -static void -initialize_crc32 (void) -{ - int byte; - - for (byte = 0; byte < 256; ++ byte) - { - enet_uint32 crc = reflect_crc (byte, 8) << 24; - int offset; + 0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x5005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0xBDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; - for(offset = 0; offset < 8; ++ offset) - { - if (crc & 0x80000000) - crc = (crc << 1) ^ 0x04c11db7; - else - crc <<= 1; - } - - crcTable [byte] = reflect_crc (crc, 32); - } - - initializedCRC32 = 1; -} - enet_uint32 enet_crc32 (const ENetBuffer * buffers, size_t bufferCount) { enet_uint32 crc = 0xFFFFFFFF; - - if (! initializedCRC32) initialize_crc32 (); while (bufferCount -- > 0) { @@ -153,7 +146,7 @@ enet_crc32 (const ENetBuffer * buffers, size_t bufferCount) while (data < dataEnd) { - crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++]; + crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++]; } ++ buffers; diff --git a/thirdparty/enet/peer.c b/thirdparty/enet/peer.c index 9370ef4be1..a7ac012079 100644 --- a/thirdparty/enet/peer.c +++ b/thirdparty/enet/peer.c @@ -90,6 +90,13 @@ enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt) } /** Queues a packet to be sent. + + On success, ENet will assume ownership of the packet, and so enet_packet_destroy + should not be called on it thereafter. On failure, the caller still must destroy + the packet on its own as ENet has not queued the packet. The caller can also + check the packet's referenceCount field after sending to check if ENet queued + the packet and thus incremented the referenceCount. + @param peer destination for the packet @param channelID channel on which to send @param packet packet to send @@ -99,7 +106,7 @@ enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt) int enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) { - ENetChannel * channel = & peer -> channels [channelID]; + ENetChannel * channel; ENetProtocol command; size_t fragmentLength; @@ -108,6 +115,7 @@ enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) packet -> dataLength > peer -> host -> maximumPacketSize) return -1; + channel = & peer -> channels [channelID]; fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment); if (peer -> host -> checksum != NULL) fragmentLength -= sizeof(enet_uint32); @@ -320,8 +328,8 @@ enet_peer_reset_queues (ENetPeer * peer) enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements))); enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands); - enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands); enet_peer_reset_outgoing_commands (& peer -> outgoingCommands); + enet_peer_reset_outgoing_commands (& peer -> outgoingSendReliableCommands); enet_peer_reset_incoming_commands (& peer -> dispatchedCommands); if (peer -> channels != NULL && peer -> channelCount > 0) @@ -563,6 +571,17 @@ enet_peer_disconnect (ENetPeer * peer, enet_uint32 data) } } +int +enet_peer_has_outgoing_commands (ENetPeer * peer) +{ + if (enet_list_empty (& peer -> outgoingCommands) && + enet_list_empty (& peer -> outgoingSendReliableCommands) && + enet_list_empty (& peer -> sentReliableCommands)) + return 0; + + return 1; +} + /** Request a disconnection from a peer, but only after all queued outgoing packets are sent. @param peer peer to request a disconnection @param data data describing the disconnection @@ -573,8 +592,7 @@ void enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data) { if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) && - ! (enet_list_empty (& peer -> outgoingCommands) && - enet_list_empty (& peer -> sentReliableCommands))) + enet_peer_has_outgoing_commands (peer)) { peer -> state = ENET_PEER_STATE_DISCONNECT_LATER; peer -> eventData = data; @@ -618,8 +636,6 @@ enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command, void enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoingCommand) { - ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID]; - peer -> outgoingDataTotal += enet_protocol_command_size (outgoingCommand -> command.header.command) + outgoingCommand -> fragmentLength; if (outgoingCommand -> command.header.channelID == 0xFF) @@ -630,36 +646,40 @@ enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoin outgoingCommand -> unreliableSequenceNumber = 0; } else - if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) { - ++ channel -> outgoingReliableSequenceNumber; - channel -> outgoingUnreliableSequenceNumber = 0; + ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID]; - outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; - outgoingCommand -> unreliableSequenceNumber = 0; - } - else - if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED) - { - ++ peer -> outgoingUnsequencedGroup; + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) + { + ++ channel -> outgoingReliableSequenceNumber; + channel -> outgoingUnreliableSequenceNumber = 0; - outgoingCommand -> reliableSequenceNumber = 0; - outgoingCommand -> unreliableSequenceNumber = 0; - } - else - { - if (outgoingCommand -> fragmentOffset == 0) - ++ channel -> outgoingUnreliableSequenceNumber; - - outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; - outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber; + outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED) + { + ++ peer -> outgoingUnsequencedGroup; + + outgoingCommand -> reliableSequenceNumber = 0; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + { + if (outgoingCommand -> fragmentOffset == 0) + ++ channel -> outgoingUnreliableSequenceNumber; + + outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber; + } } - + outgoingCommand -> sendAttempts = 0; outgoingCommand -> sentTime = 0; outgoingCommand -> roundTripTimeout = 0; - outgoingCommand -> roundTripTimeoutLimit = 0; outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber); + outgoingCommand -> queueTime = ++ peer -> host -> totalQueued; switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) { @@ -670,12 +690,16 @@ enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoin case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup); break; - + default: break; } - enet_list_insert (enet_list_end (& peer -> outgoingCommands), outgoingCommand); + if ((outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0 && + outgoingCommand -> packet != NULL) + enet_list_insert (enet_list_end (& peer -> outgoingSendReliableCommands), outgoingCommand); + else + enet_list_insert (enet_list_end (& peer -> outgoingCommands), outgoingCommand); } ENetOutgoingCommand * diff --git a/thirdparty/enet/protocol.c b/thirdparty/enet/protocol.c index d7fe80f117..af307af7e5 100644 --- a/thirdparty/enet/protocol.c +++ b/thirdparty/enet/protocol.c @@ -9,7 +9,7 @@ #include "enet/time.h" #include "enet/enet.h" -static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] = +static const size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] = { 0, sizeof (ENetProtocolAcknowledge), @@ -159,16 +159,16 @@ enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * e } static void -enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer) +enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer, ENetList * sentUnreliableCommands) { ENetOutgoingCommand * outgoingCommand; - if (enet_list_empty (& peer -> sentUnreliableCommands)) + if (enet_list_empty (sentUnreliableCommands)) return; do { - outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands); + outgoingCommand = (ENetOutgoingCommand *) enet_list_front (sentUnreliableCommands); enet_list_remove (& outgoingCommand -> outgoingCommandList); @@ -185,14 +185,38 @@ enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer) } enet_free (outgoingCommand); - } while (! enet_list_empty (& peer -> sentUnreliableCommands)); + } while (! enet_list_empty (sentUnreliableCommands)); if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER && - enet_list_empty (& peer -> outgoingCommands) && - enet_list_empty (& peer -> sentReliableCommands)) + ! enet_peer_has_outgoing_commands (peer)) enet_peer_disconnect (peer, peer -> eventData); } +static ENetOutgoingCommand * +enet_protocol_find_sent_reliable_command (ENetList * list, enet_uint16 reliableSequenceNumber, enet_uint8 channelID) +{ + ENetListIterator currentCommand; + + for (currentCommand = enet_list_begin (list); + currentCommand != enet_list_end (list); + currentCommand = enet_list_next (currentCommand)) + { + ENetOutgoingCommand * outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (! (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)) + continue; + + if (outgoingCommand -> sendAttempts < 1) + break; + + if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && + outgoingCommand -> command.header.channelID == channelID) + return outgoingCommand; + } + + return NULL; +} + static ENetProtocolCommand enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID) { @@ -214,24 +238,9 @@ enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliabl if (currentCommand == enet_list_end (& peer -> sentReliableCommands)) { - for (currentCommand = enet_list_begin (& peer -> outgoingCommands); - currentCommand != enet_list_end (& peer -> outgoingCommands); - currentCommand = enet_list_next (currentCommand)) - { - outgoingCommand = (ENetOutgoingCommand *) currentCommand; - - if (! (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)) - continue; - - if (outgoingCommand -> sendAttempts < 1) return ENET_PROTOCOL_COMMAND_NONE; - - if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && - outgoingCommand -> command.header.channelID == channelID) - break; - } - - if (currentCommand == enet_list_end (& peer -> outgoingCommands)) - return ENET_PROTOCOL_COMMAND_NONE; + outgoingCommand = enet_protocol_find_sent_reliable_command (& peer -> outgoingCommands, reliableSequenceNumber, channelID); + if (outgoingCommand == NULL) + outgoingCommand = enet_protocol_find_sent_reliable_command (& peer -> outgoingSendReliableCommands, reliableSequenceNumber, channelID); wasSent = 0; } @@ -331,6 +340,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT; peer -> connectID = command -> connect.connectID; peer -> address = host -> receivedAddress; + peer -> mtu = host -> mtu; peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID); peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth); peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth); @@ -375,7 +385,8 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) mtu = ENET_PROTOCOL_MAXIMUM_MTU; - peer -> mtu = mtu; + if (mtu < peer -> mtu) + peer -> mtu = mtu; if (host -> outgoingBandwidth == 0 && peer -> incomingBandwidth == 0) @@ -542,7 +553,8 @@ enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENet fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength); * currentData += fragmentLength; - if (fragmentLength > host -> maximumPacketSize || + if (fragmentLength <= 0 || + fragmentLength > host -> maximumPacketSize || * currentData < host -> receivedData || * currentData > & host -> receivedData [host -> receivedDataLength]) return -1; @@ -566,6 +578,7 @@ enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENet if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || fragmentNumber >= fragmentCount || totalLength > host -> maximumPacketSize || + totalLength < fragmentCount || fragmentOffset >= totalLength || fragmentLength > totalLength - fragmentOffset) return -1; @@ -921,8 +934,7 @@ enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * break; case ENET_PEER_STATE_DISCONNECT_LATER: - if (enet_list_empty (& peer -> outgoingCommands) && - enet_list_empty (& peer -> sentReliableCommands)) + if (! enet_peer_has_outgoing_commands (peer)) enet_peer_disconnect (peer, peer -> eventData); break; @@ -1230,6 +1242,9 @@ enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event) & buffer, 1); + if (receivedLength == -2) + continue; + if (receivedLength < 0) return -1; @@ -1293,7 +1308,7 @@ enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer) buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge)) { - host -> continueSending = 1; + peer -> flags |= ENET_PEER_FLAG_CONTINUE_SENDING; break; } @@ -1333,10 +1348,11 @@ static int enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event) { ENetOutgoingCommand * outgoingCommand; - ENetListIterator currentCommand, insertPosition; + ENetListIterator currentCommand, insertPosition, insertSendReliablePosition; currentCommand = enet_list_begin (& peer -> sentReliableCommands); insertPosition = enet_list_begin (& peer -> outgoingCommands); + insertSendReliablePosition = enet_list_begin (& peer -> outgoingSendReliableCommands); while (currentCommand != enet_list_end (& peer -> sentReliableCommands)) { @@ -1353,7 +1369,7 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even if (peer -> earliestTimeout != 0 && (ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMaximum || - (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit && + ((1 << (outgoingCommand -> sendAttempts - 1)) >= peer -> timeoutLimit && ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMinimum))) { enet_protocol_notify_disconnect (host, peer, event); @@ -1361,14 +1377,18 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even return 1; } - if (outgoingCommand -> packet != NULL) - peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; - ++ peer -> packetsLost; outgoingCommand -> roundTripTimeout *= 2; - enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList)); + if (outgoingCommand -> packet != NULL) + { + peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; + + enet_list_insert (insertSendReliablePosition, enet_list_remove (& outgoingCommand -> outgoingCommandList)); + } + else + enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList)); if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) && ! enet_list_empty (& peer -> sentReliableCommands)) @@ -1383,22 +1403,41 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even } static int -enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) +enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer, ENetList * sentUnreliableCommands) { ENetProtocol * command = & host -> commands [host -> commandCount]; ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; ENetOutgoingCommand * outgoingCommand; - ENetListIterator currentCommand; - ENetChannel *channel; - enet_uint16 reliableWindow; + ENetListIterator currentCommand, currentSendReliableCommand; + ENetChannel *channel = NULL; + enet_uint16 reliableWindow = 0; size_t commandSize; - int windowExceeded = 0, windowWrap = 0, canPing = 1; + int windowWrap = 0, canPing = 1; currentCommand = enet_list_begin (& peer -> outgoingCommands); - - while (currentCommand != enet_list_end (& peer -> outgoingCommands)) + currentSendReliableCommand = enet_list_begin (& peer -> outgoingSendReliableCommands); + + for (;;) { - outgoingCommand = (ENetOutgoingCommand *) currentCommand; + if (currentCommand != enet_list_end (& peer -> outgoingCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (currentSendReliableCommand != enet_list_end (& peer -> outgoingSendReliableCommands) && + ENET_TIME_LESS (((ENetOutgoingCommand *) currentSendReliableCommand) -> queueTime, outgoingCommand -> queueTime)) + goto useSendReliableCommand; + + currentCommand = enet_list_next (currentCommand); + } + else + if (currentSendReliableCommand != enet_list_end (& peer -> outgoingSendReliableCommands)) + { + useSendReliableCommand: + outgoingCommand = (ENetOutgoingCommand *) currentSendReliableCommand; + currentSendReliableCommand = enet_list_next (currentSendReliableCommand); + } + else + break; if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) { @@ -1406,33 +1445,29 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (channel != NULL) { - if (! windowWrap && - outgoingCommand -> sendAttempts < 1 && + if (windowWrap) + continue; + else + if (outgoingCommand -> sendAttempts < 1 && ! (outgoingCommand -> reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) && (channel -> reliableWindows [(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE || channel -> usedReliableWindows & ((((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) << reliableWindow) | (((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow))))) - windowWrap = 1; - if (windowWrap) { - currentCommand = enet_list_next (currentCommand); - + windowWrap = 1; + currentSendReliableCommand = enet_list_end (& peer -> outgoingSendReliableCommands); + continue; } } - + if (outgoingCommand -> packet != NULL) { - if (! windowExceeded) - { - enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE; - - if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu)) - windowExceeded = 1; - } - if (windowExceeded) + enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE; + + if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu)) { - currentCommand = enet_list_next (currentCommand); + currentSendReliableCommand = enet_list_end (& peer -> outgoingSendReliableCommands); continue; } @@ -1448,13 +1483,11 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) (outgoingCommand -> packet != NULL && (enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength))) { - host -> continueSending = 1; - + peer -> flags |= ENET_PEER_FLAG_CONTINUE_SENDING; + break; } - currentCommand = enet_list_next (currentCommand); - if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) { if (channel != NULL && outgoingCommand -> sendAttempts < 1) @@ -1466,10 +1499,7 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) ++ outgoingCommand -> sendAttempts; if (outgoingCommand -> roundTripTimeout == 0) - { - outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance; - outgoingCommand -> roundTripTimeoutLimit = peer -> timeoutLimit * outgoingCommand -> roundTripTimeout; - } + outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance; if (enet_list_empty (& peer -> sentReliableCommands)) peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout; @@ -1522,7 +1552,7 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) enet_list_remove (& outgoingCommand -> outgoingCommandList); if (outgoingCommand -> packet != NULL) - enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand); + enet_list_insert (enet_list_end (sentUnreliableCommands), outgoingCommand); } buffer -> data = command; @@ -1555,9 +1585,8 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) host -> bufferCount = buffer - host -> buffers; if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER && - enet_list_empty (& peer -> outgoingCommands) && - enet_list_empty (& peer -> sentReliableCommands) && - enet_list_empty (& peer -> sentUnreliableCommands)) + ! enet_peer_has_outgoing_commands (peer) && + enet_list_empty (sentUnreliableCommands)) enet_peer_disconnect (peer, peer -> eventData); return canPing; @@ -1568,22 +1597,24 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch { enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)]; ENetProtocolHeader * header = (ENetProtocolHeader *) headerData; - ENetPeer * currentPeer; - int sentLength; + int sentLength = 0; size_t shouldCompress = 0; - - host -> continueSending = 1; + ENetList sentUnreliableCommands; - while (host -> continueSending) - for (host -> continueSending = 0, - currentPeer = host -> peers; + enet_list_clear (& sentUnreliableCommands); + + for (int sendPass = 0, continueSending = 0; sendPass <= continueSending; ++ sendPass) + for (ENetPeer * currentPeer = host -> peers; currentPeer < & host -> peers [host -> peerCount]; ++ currentPeer) { if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED || - currentPeer -> state == ENET_PEER_STATE_ZOMBIE) + currentPeer -> state == ENET_PEER_STATE_ZOMBIE || + (sendPass > 0 && ! (currentPeer -> flags & ENET_PEER_FLAG_CONTINUE_SENDING))) continue; + currentPeer -> flags &= ~ ENET_PEER_FLAG_CONTINUE_SENDING; + host -> headerFlags = 0; host -> commandCount = 0; host -> bufferCount = 1; @@ -1600,21 +1631,22 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) return 1; else - continue; + goto nextPeer; } - if ((enet_list_empty (& currentPeer -> outgoingCommands) || - enet_protocol_check_outgoing_commands (host, currentPeer)) && + if (((enet_list_empty (& currentPeer -> outgoingCommands) && + enet_list_empty (& currentPeer -> outgoingSendReliableCommands)) || + enet_protocol_check_outgoing_commands (host, currentPeer, & sentUnreliableCommands)) && enet_list_empty (& currentPeer -> sentReliableCommands) && ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval && currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing)) { enet_peer_ping (currentPeer); - enet_protocol_check_outgoing_commands (host, currentPeer); + enet_protocol_check_outgoing_commands (host, currentPeer, & sentUnreliableCommands); } if (host -> commandCount == 0) - continue; + goto nextPeer; if (currentPeer -> packetLossEpoch == 0) currentPeer -> packetLossEpoch = host -> serviceTime; @@ -1625,7 +1657,7 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent; #ifdef ENET_DEBUG - printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0); + printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingCommands) + enet_list_size (& currentPeer -> outgoingSendReliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0); #endif currentPeer -> packetLossVariance = (currentPeer -> packetLossVariance * 3 + ENET_DIFFERENCE (packetLoss, currentPeer -> packetLoss)) / 4; @@ -1687,13 +1719,17 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount); - enet_protocol_remove_sent_unreliable_commands (currentPeer); + enet_protocol_remove_sent_unreliable_commands (currentPeer, & sentUnreliableCommands); if (sentLength < 0) return -1; host -> totalSentData += sentLength; host -> totalSentPackets ++; + + nextPeer: + if (currentPeer -> flags & ENET_PEER_FLAG_CONTINUE_SENDING) + continueSending = sendPass + 1; } return 0; |