diff options
32 files changed, 837 insertions, 352 deletions
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 74c5c1c191..265d9ef56c 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -327,7 +327,7 @@ uint64_t FileAccessPack::get_buffer(uint8_t *p_dst, uint64_t p_length) const { to_read = (int64_t)pf.size - (int64_t)pos; } - pos += p_length; + pos += to_read; if (to_read <= 0) { return 0; diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h index dbc1cb31de..9b49fcc3c8 100644 --- a/core/math/dynamic_bvh.h +++ b/core/math/dynamic_bvh.h @@ -328,7 +328,8 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) { volume.min = p_box.position; volume.max = p_box.position + p_box.size; - const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **stack = alloca_stack; stack[0] = bvh_root; int32_t depth = 1; int32_t threshold = ALLOCA_STACK_SIZE - 2; @@ -343,7 +344,8 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) { if (depth > threshold) { if (aux_stack.is_empty()) { aux_stack.resize(ALLOCA_STACK_SIZE * 2); - memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + alloca_stack = nullptr; } else { aux_stack.resize(aux_stack.size() * 2); } @@ -384,7 +386,8 @@ void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Ve } } - const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **stack = alloca_stack; stack[0] = bvh_root; int32_t depth = 1; int32_t threshold = ALLOCA_STACK_SIZE - 2; @@ -399,7 +402,8 @@ void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Ve if (depth > threshold) { if (aux_stack.is_empty()) { aux_stack.resize(ALLOCA_STACK_SIZE * 2); - memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + alloca_stack = nullptr; } else { aux_stack.resize(aux_stack.size() * 2); } @@ -436,7 +440,8 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu Vector3 bounds[2]; - const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **stack = alloca_stack; stack[0] = bvh_root; int32_t depth = 1; int32_t threshold = ALLOCA_STACK_SIZE - 2; @@ -456,7 +461,8 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu if (depth > threshold) { if (aux_stack.is_empty()) { aux_stack.resize(ALLOCA_STACK_SIZE * 2); - memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + alloca_stack = nullptr; } else { aux_stack.resize(aux_stack.size() * 2); } diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 3b274ecc2f..0b2d5e41cf 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -138,6 +138,8 @@ void Script::_bind_methods() { ClassDB::bind_method(D_METHOD("get_base_script"), &Script::get_base_script); ClassDB::bind_method(D_METHOD("get_instance_base_type"), &Script::get_instance_base_type); + ClassDB::bind_method(D_METHOD("get_global_name"), &Script::get_global_name); + ClassDB::bind_method(D_METHOD("has_script_signal", "signal_name"), &Script::has_script_signal); ClassDB::bind_method(D_METHOD("get_script_property_list"), &Script::_get_script_property_list); diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 116210e8de..171074188f 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -1302,192 +1302,192 @@ struct VariantInitializer<Object *> { }; template <class T> -struct VariantZeroAssigner { +struct VariantDefaultInitializer { }; template <> -struct VariantZeroAssigner<bool> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_bool(v) = false; } +struct VariantDefaultInitializer<bool> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_bool(v) = false; } }; template <> -struct VariantZeroAssigner<int64_t> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int(v) = 0; } +struct VariantDefaultInitializer<int64_t> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_int(v) = 0; } }; template <> -struct VariantZeroAssigner<double> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float(v) = 0.0; } +struct VariantDefaultInitializer<double> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float(v) = 0.0; } }; template <> -struct VariantZeroAssigner<float> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float(v) = 0.0; } +struct VariantDefaultInitializer<float> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float(v) = 0.0; } }; template <> -struct VariantZeroAssigner<String> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string(v) = String(); } +struct VariantDefaultInitializer<String> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_string(v) = String(); } }; template <> -struct VariantZeroAssigner<Vector2> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2(v) = Vector2(); } +struct VariantDefaultInitializer<Vector2> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector2(v) = Vector2(); } }; template <> -struct VariantZeroAssigner<Vector2i> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2i(v) = Vector2i(); } +struct VariantDefaultInitializer<Vector2i> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector2i(v) = Vector2i(); } }; template <> -struct VariantZeroAssigner<Rect2> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rect2(v) = Rect2(); } +struct VariantDefaultInitializer<Rect2> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_rect2(v) = Rect2(); } }; template <> -struct VariantZeroAssigner<Rect2i> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rect2i(v) = Rect2i(); } +struct VariantDefaultInitializer<Rect2i> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_rect2i(v) = Rect2i(); } }; template <> -struct VariantZeroAssigner<Vector3> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3(v) = Vector3(); } +struct VariantDefaultInitializer<Vector3> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector3(v) = Vector3(); } }; template <> -struct VariantZeroAssigner<Vector3i> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3i(v) = Vector3i(); } +struct VariantDefaultInitializer<Vector3i> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector3i(v) = Vector3i(); } }; template <> -struct VariantZeroAssigner<Vector4> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector4(v) = Vector4(); } +struct VariantDefaultInitializer<Vector4> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector4(v) = Vector4(); } }; template <> -struct VariantZeroAssigner<Vector4i> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector4i(v) = Vector4i(); } +struct VariantDefaultInitializer<Vector4i> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector4i(v) = Vector4i(); } }; template <> -struct VariantZeroAssigner<Transform2D> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform2d(v) = Transform2D(); } +struct VariantDefaultInitializer<Transform2D> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_transform2d(v) = Transform2D(); } }; template <> -struct VariantZeroAssigner<Plane> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_plane(v) = Plane(); } +struct VariantDefaultInitializer<Plane> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_plane(v) = Plane(); } }; template <> -struct VariantZeroAssigner<Quaternion> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_quaternion(v) = Quaternion(); } +struct VariantDefaultInitializer<Quaternion> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_quaternion(v) = Quaternion(); } }; template <> -struct VariantZeroAssigner<AABB> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_aabb(v) = AABB(); } +struct VariantDefaultInitializer<AABB> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_aabb(v) = AABB(); } }; template <> -struct VariantZeroAssigner<Basis> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_basis(v) = Basis(); } +struct VariantDefaultInitializer<Basis> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_basis(v) = Basis(); } }; template <> -struct VariantZeroAssigner<Transform3D> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform(v) = Transform3D(); } +struct VariantDefaultInitializer<Transform3D> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_transform(v) = Transform3D(); } }; template <> -struct VariantZeroAssigner<Projection> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_projection(v) = Projection(); } +struct VariantDefaultInitializer<Projection> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_projection(v) = Projection(); } }; template <> -struct VariantZeroAssigner<Color> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_color(v) = Color(); } +struct VariantDefaultInitializer<Color> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_color(v) = Color(); } }; template <> -struct VariantZeroAssigner<StringName> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string_name(v) = StringName(); } +struct VariantDefaultInitializer<StringName> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_string_name(v) = StringName(); } }; template <> -struct VariantZeroAssigner<NodePath> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_node_path(v) = NodePath(); } +struct VariantDefaultInitializer<NodePath> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_node_path(v) = NodePath(); } }; template <> -struct VariantZeroAssigner<::RID> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rid(v) = RID(); } +struct VariantDefaultInitializer<::RID> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_rid(v) = RID(); } }; template <> -struct VariantZeroAssigner<Callable> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_callable(v) = Callable(); } +struct VariantDefaultInitializer<Callable> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_callable(v) = Callable(); } }; template <> -struct VariantZeroAssigner<Signal> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_signal(v) = Signal(); } +struct VariantDefaultInitializer<Signal> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_signal(v) = Signal(); } }; template <> -struct VariantZeroAssigner<Dictionary> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_dictionary(v) = Dictionary(); } +struct VariantDefaultInitializer<Dictionary> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_dictionary(v) = Dictionary(); } }; template <> -struct VariantZeroAssigner<Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_array(v) = Array(); } +struct VariantDefaultInitializer<Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_array(v) = Array(); } }; template <> -struct VariantZeroAssigner<PackedByteArray> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_byte_array(v) = PackedByteArray(); } +struct VariantDefaultInitializer<PackedByteArray> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_byte_array(v) = PackedByteArray(); } }; template <> -struct VariantZeroAssigner<PackedInt32Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int32_array(v) = PackedInt32Array(); } +struct VariantDefaultInitializer<PackedInt32Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_int32_array(v) = PackedInt32Array(); } }; template <> -struct VariantZeroAssigner<PackedInt64Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int64_array(v) = PackedInt64Array(); } +struct VariantDefaultInitializer<PackedInt64Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_int64_array(v) = PackedInt64Array(); } }; template <> -struct VariantZeroAssigner<PackedFloat32Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float32_array(v) = PackedFloat32Array(); } +struct VariantDefaultInitializer<PackedFloat32Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float32_array(v) = PackedFloat32Array(); } }; template <> -struct VariantZeroAssigner<PackedFloat64Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float64_array(v) = PackedFloat64Array(); } +struct VariantDefaultInitializer<PackedFloat64Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float64_array(v) = PackedFloat64Array(); } }; template <> -struct VariantZeroAssigner<PackedStringArray> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string_array(v) = PackedStringArray(); } +struct VariantDefaultInitializer<PackedStringArray> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_string_array(v) = PackedStringArray(); } }; template <> -struct VariantZeroAssigner<PackedVector2Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2_array(v) = PackedVector2Array(); } +struct VariantDefaultInitializer<PackedVector2Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector2_array(v) = PackedVector2Array(); } }; template <> -struct VariantZeroAssigner<PackedVector3Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3_array(v) = PackedVector3Array(); } +struct VariantDefaultInitializer<PackedVector3Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector3_array(v) = PackedVector3Array(); } }; template <> -struct VariantZeroAssigner<PackedColorArray> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_color_array(v) = PackedColorArray(); } +struct VariantDefaultInitializer<PackedColorArray> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_color_array(v) = PackedColorArray(); } }; template <class T> @@ -1504,7 +1504,7 @@ struct VariantTypeChanger { VariantInitializer<T>::init(v); } - VariantZeroAssigner<T>::zero(v); + VariantDefaultInitializer<T>::init(v); } }; diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml index 6fa8e4ae9f..98110748ef 100644 --- a/doc/classes/Area2D.xml +++ b/doc/classes/Area2D.xml @@ -6,6 +6,7 @@ <description> [Area2D] is a region of 2D space defined by one or multiple [CollisionShape2D] or [CollisionPolygon2D] child nodes. It detects when other [CollisionObject2D]s enter or exit it, and it also keeps track of which collision objects haven't exited it yet (i.e. which one are overlapping it). This node can also locally alter or override physics parameters (gravity, damping) and route audio to custom audio buses. + [b]Note:[/b] Areas and bodies created with [PhysicsServer2D] might not interact as expected with [Area2D]s, and might not emit signals or track objects correctly. </description> <tutorials> <link title="Using Area2D">$DOCS_URL/tutorials/physics/using_area_2d.html</link> diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml index 4f89e9b015..918869b83d 100644 --- a/doc/classes/Area3D.xml +++ b/doc/classes/Area3D.xml @@ -6,6 +6,7 @@ <description> [Area3D] is a region of 3D space defined by one or multiple [CollisionShape3D] or [CollisionPolygon3D] child nodes. It detects when other [CollisionObject3D]s enter or exit it, and it also keeps track of which collision objects haven't exited it yet (i.e. which one are overlapping it). This node can also locally alter or override physics parameters (gravity, damping) and route audio to custom audio buses. + [b]Note:[/b] Areas and bodies created with [PhysicsServer3D] might not interact as expected with [Area3D]s, and might not emit signals or track objects correctly. [b]Warning:[/b] Using a [ConcavePolygonShape3D] inside a [CollisionShape3D] child of this node (created e.g. by using the [b]Create Trimesh Collision Sibling[/b] option in the [b]Mesh[/b] menu that appears when selecting a [MeshInstance3D] node) may give unexpected results, since this collision shape is hollow. If this is not desired, it has to be split into multiple [ConvexPolygonShape3D]s or primitive shapes like [BoxShape3D], or in some cases it may be replaceable by a [CollisionPolygon3D]. </description> <tutorials> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 7f7dc1d288..b8f82accfc 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -235,6 +235,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -258,6 +261,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -281,6 +287,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -305,6 +314,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -327,6 +339,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -353,6 +368,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -376,6 +394,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -391,6 +412,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -408,6 +432,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -421,6 +448,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -549,6 +579,13 @@ [b]Note:[/b] This method is implemented only on macOS. </description> </method> + <method name="global_menu_get_system_menu_roots" qualifiers="const"> + <return type="Dictionary" /> + <description> + Returns Dictionary of supported system menu IDs and names. + [b]Note:[/b] This method is implemented only on macOS. + </description> + </method> <method name="global_menu_is_item_checkable" qualifiers="const"> <return type="bool" /> <param index="0" name="menu_root" type="String" /> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index 3f4ec1b677..40d5a9f4a2 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -346,6 +346,12 @@ Returns [code]true[/code] if the specified item's shortcut is disabled. </description> </method> + <method name="is_system_menu" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the menu is bound to the special system menu. + </description> + </method> <method name="remove_item"> <return type="void" /> <param index="0" name="index" type="int" /> @@ -566,6 +572,9 @@ <member name="submenu_popup_delay" type="float" setter="set_submenu_popup_delay" getter="get_submenu_popup_delay" default="0.3"> Sets the delay time in seconds for the submenu item to popup on mouse hovering. If the popup menu is added as a child of another (acting as a submenu), it will inherit the delay time of the parent menu item. </member> + <member name="system_menu_root" type="String" setter="set_system_menu_root" getter="get_system_menu_root" default=""""> + If set to one of the values returned by [method DisplayServer.global_menu_get_system_menu_roots], this [PopupMenu] is bound to the special system menu. Only one [PopupMenu] can be bound to each special menu at a time. + </member> </members> <signals> <signal name="id_focused"> diff --git a/doc/classes/Script.xml b/doc/classes/Script.xml index 28d763585d..fa8e4ef5f2 100644 --- a/doc/classes/Script.xml +++ b/doc/classes/Script.xml @@ -24,6 +24,27 @@ Returns the script directly inherited by this script. </description> </method> + <method name="get_global_name" qualifiers="const"> + <return type="StringName" /> + <description> + Returns the class name associated with the script, if there is one. Returns an empty string otherwise. + To give the script a global name, you can use the [code]class_name[/code] keyword in GDScript and the [code][GlobalClass][/code] attribute in C#. + [codeblocks] + [gdscript] + class_name MyNode + extends Node + [/gdscript] + [csharp] + using Godot; + + [GlobalClass] + public partial class MyNode : Node + { + } + [/csharp] + [/codeblocks] + </description> + </method> <method name="get_instance_base_type" qualifiers="const"> <return type="StringName" /> <description> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index b6a407e042..4d0f453fe0 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -128,6 +128,13 @@ Returns the [Control] having the focus within this viewport. If no [Control] has the focus, returns null. </description> </method> + <method name="gui_get_hovered_control" qualifiers="const"> + <return type="Control" /> + <description> + Returns the [Control] that the mouse is currently hovering over in this viewport. If no [Control] has the cursor, returns null. + Typically the leaf [Control] node or deepest level of the subtree which claims hover. This is very useful when used together with [method Node.is_ancestor_of] to find if the mouse is within a control tree. + </description> + </method> <method name="gui_is_drag_successful" qualifiers="const"> <return type="bool" /> <description> diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 52f432135e..c514ca7ba7 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3692,7 +3692,7 @@ void EditorNode::_set_current_scene_nocheck(int p_idx) { _edit_current(true); _update_title(); - scene_tabs->update_scene_tabs(); + callable_mp(scene_tabs, &EditorSceneTabs::update_scene_tabs).call_deferred(); if (tabs_to_close.is_empty()) { call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); // Do after everything else is done setting up. @@ -7357,6 +7357,20 @@ EditorNode::EditorNode() { file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/file_quit", TTR("Quit"), KeyModifierMask::CMD_OR_CTRL + Key::Q), FILE_QUIT, true); } + ED_SHORTCUT_AND_COMMAND("editor/editor_settings", TTR("Editor Settings...")); + ED_SHORTCUT_OVERRIDE("editor/editor_settings", "macos", KeyModifierMask::META + Key::COMMA); +#ifdef MACOS_ENABLED + if (global_menu) { + apple_menu = memnew(PopupMenu); + apple_menu->set_system_menu_root("_apple"); + main_menu->add_child(apple_menu); + + apple_menu->add_shortcut(ED_GET_SHORTCUT("editor/editor_settings"), SETTINGS_PREFERENCES); + apple_menu->add_separator(); + apple_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); + } +#endif + project_menu = memnew(PopupMenu); project_menu->set_name(TTR("Project")); main_menu->add_child(project_menu); @@ -7422,9 +7436,13 @@ EditorNode::EditorNode() { settings_menu->set_name(TTR("Editor")); main_menu->add_child(settings_menu); - ED_SHORTCUT_AND_COMMAND("editor/editor_settings", TTR("Editor Settings...")); - ED_SHORTCUT_OVERRIDE("editor/editor_settings", "macos", KeyModifierMask::META + Key::COMMA); +#ifdef MACOS_ENABLED + if (!global_menu) { + settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/editor_settings"), SETTINGS_PREFERENCES); + } +#else settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/editor_settings"), SETTINGS_PREFERENCES); +#endif settings_menu->add_shortcut(ED_SHORTCUT("editor/command_palette", TTR("Command Palette..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::P), HELP_COMMAND_PALETTE); settings_menu->add_separator(); @@ -7471,6 +7489,7 @@ EditorNode::EditorNode() { help_menu = memnew(PopupMenu); help_menu->set_name(TTR("Help")); + help_menu->set_system_menu_root("_help"); main_menu->add_child(help_menu); help_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); diff --git a/editor/editor_node.h b/editor/editor_node.h index 8b440b798c..c72a8f9324 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -346,6 +346,7 @@ private: EditorRunBar *project_run_bar = nullptr; VBoxContainer *main_screen_vbox = nullptr; MenuBar *main_menu = nullptr; + PopupMenu *apple_menu = nullptr; PopupMenu *file_menu = nullptr; PopupMenu *project_menu = nullptr; PopupMenu *debug_menu = nullptr; diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index e917aab6ad..c1a65b7bb7 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -548,7 +548,7 @@ void ScriptEditor::_set_breakpoint(Ref<RefCounted> p_script, int p_line, bool p_ } state["breakpoints"] = breakpoints; script_editor_cache->set_value(scr->get_path(), "state", state); - EditorDebuggerNode::get_singleton()->set_breakpoint(scr->get_path(), p_line + 1, false); + EditorDebuggerNode::get_singleton()->set_breakpoint(scr->get_path(), p_line + 1, p_enabled); } } diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 9ea0a03e68..6c153f6113 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -2287,8 +2287,11 @@ VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) { item_add_edit->connect("text_submitted", callable_mp(this, &ThemeTypeEditor::_item_add_lineedit_cbk).bind(p_data_type, item_add_edit)); Button *item_add_button = memnew(Button); item_add_button->set_text(TTR("Add")); + item_add_button->set_disabled(true); item_add_hb->add_child(item_add_button); item_add_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_add_cbk).bind(p_data_type, item_add_edit)); + item_add_edit->set_meta("button", item_add_button); + item_add_edit->connect("text_changed", callable_mp(this, &ThemeTypeEditor::_update_add_button).bind(item_add_edit)); return items_list; } @@ -2850,6 +2853,11 @@ void ThemeTypeEditor::_add_default_type_items() { ur->commit_action(); } +void ThemeTypeEditor::_update_add_button(const String &p_text, LineEdit *p_for_edit) { + Button *button = Object::cast_to<Button>(p_for_edit->get_meta("button")); + button->set_disabled(p_text.strip_edges().is_empty()); +} + void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) { LineEdit *le = Object::cast_to<LineEdit>(p_control); if (le->get_text().strip_edges().is_empty()) { @@ -2895,6 +2903,7 @@ void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) { ur->commit_action(); le->set_text(""); + _update_add_button("", le); } void ThemeTypeEditor::_item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control) { diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 33accf587a..cf8c5ceb28 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -382,6 +382,7 @@ class ThemeTypeEditor : public MarginContainer { void _add_type_button_cbk(); void _add_default_type_items(); + void _update_add_button(const String &p_text, LineEdit *p_for_edit); void _item_add_cbk(int p_data_type, Control *p_control); void _item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control); void _item_override_cbk(int p_data_type, String p_item_name); diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index f012292f84..a19e02eb21 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -140,6 +140,8 @@ private: String rendering_driver; NSMenu *apple_menu = nullptr; + NSMenu *window_menu = nullptr; + NSMenu *help_menu = nullptr; NSMenu *dock_menu = nullptr; struct MenuData { Callable open; @@ -226,7 +228,8 @@ private: static NSCursor *_cursor_from_selector(SEL p_selector, SEL p_fallback = nil); - bool _has_help_menu() const; + int _get_system_menu_start(const NSMenu *p_menu) const; + int _get_system_menu_count(const NSMenu *p_menu) const; NSMenuItem *_menu_add_item(const String &p_menu_root, const String &p_label, Key p_accel, int p_index, int *r_out); public: @@ -321,6 +324,8 @@ public: virtual void global_menu_remove_item(const String &p_menu_root, int p_idx) override; virtual void global_menu_clear(const String &p_menu_root) override; + virtual Dictionary global_menu_get_system_menu_roots() const override; + virtual bool tts_is_speaking() const override; virtual bool tts_is_paused() const override; virtual TypedArray<Dictionary> tts_get_voices() const override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 407a315827..742b552e13 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -64,6 +64,9 @@ #import <IOKit/hid/IOHIDKeys.h> #import <IOKit/hid/IOHIDLib.h> +#define MENU_TAG_START 0x00004447 +#define MENU_TAG_END 0xFFFF4447 + const NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) const { const NSMenu *menu = nullptr; if (p_menu_root == "" || p_menu_root.to_lower() == "_main") { @@ -72,16 +75,21 @@ const NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) cons } else if (p_menu_root.to_lower() == "_dock") { // macOS dock menu. menu = dock_menu; + } else if (p_menu_root.to_lower() == "_apple") { + // macOS Apple menu. + menu = apple_menu; + } else if (p_menu_root.to_lower() == "_window") { + // macOS Window menu. + menu = window_menu; + } else if (p_menu_root.to_lower() == "_help") { + // macOS Help menu. + menu = help_menu; } else { // Submenu. if (submenu.has(p_menu_root)) { menu = submenu[p_menu_root].menu; } } - if (menu == apple_menu) { - // Do not allow to change Apple menu. - return nullptr; - } return menu; } @@ -93,6 +101,15 @@ NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) { } else if (p_menu_root.to_lower() == "_dock") { // macOS dock menu. menu = dock_menu; + } else if (p_menu_root.to_lower() == "_apple") { + // macOS Apple menu. + menu = apple_menu; + } else if (p_menu_root.to_lower() == "_window") { + // macOS Window menu. + menu = window_menu; + } else if (p_menu_root.to_lower() == "_help") { + // macOS Help menu. + menu = help_menu; } else { // Submenu. if (!submenu.has(p_menu_root)) { @@ -104,10 +121,6 @@ NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) { } menu = submenu[p_menu_root].menu; } - if (menu == apple_menu) { - // Do not allow to change Apple menu. - return nullptr; - } return menu; } @@ -820,22 +833,6 @@ String DisplayServerMacOS::get_name() const { return "macOS"; } -bool DisplayServerMacOS::_has_help_menu() const { - if ([NSApp helpMenu]) { - return true; - } else { - NSMenu *menu = [NSApp mainMenu]; - const NSMenuItem *menu_item = [menu itemAtIndex:[menu numberOfItems] - 1]; - if (menu_item) { - String menu_name = String::utf8([[menu_item title] UTF8String]); - if (menu_name == "Help" || menu_name == RTR("Help")) { - return true; - } - } - return false; - } -} - bool DisplayServerMacOS::_is_menu_opened(NSMenu *p_menu) const { if (submenu_inv.has(p_menu)) { const MenuData &md = submenu[submenu_inv[p_menu]]; @@ -854,24 +851,57 @@ bool DisplayServerMacOS::_is_menu_opened(NSMenu *p_menu) const { return false; } +int DisplayServerMacOS::_get_system_menu_start(const NSMenu *p_menu) const { + if (p_menu == [NSApp mainMenu]) { // Skip Apple menu. + return 1; + } + if (p_menu == apple_menu || p_menu == window_menu || p_menu == help_menu) { + int count = [p_menu numberOfItems]; + for (int i = 0; i < count; i++) { + NSMenuItem *menu_item = [p_menu itemAtIndex:i]; + if (menu_item.tag == MENU_TAG_START) { + return i + 1; + } + } + } + return 0; +} + +int DisplayServerMacOS::_get_system_menu_count(const NSMenu *p_menu) const { + if (p_menu == [NSApp mainMenu]) { // Skip Apple, Window and Help menu. + return [p_menu numberOfItems] - 3; + } + if (p_menu == apple_menu || p_menu == window_menu || p_menu == help_menu) { + int start = 0; + int count = [p_menu numberOfItems]; + for (int i = 0; i < count; i++) { + NSMenuItem *menu_item = [p_menu itemAtIndex:i]; + if (menu_item.tag == MENU_TAG_START) { + start = i + 1; + } + if (menu_item.tag == MENU_TAG_END) { + return i - start; + } + } + } + return [p_menu numberOfItems]; +} + NSMenuItem *DisplayServerMacOS::_menu_add_item(const String &p_menu_root, const String &p_label, Key p_accel, int p_index, int *r_out) { NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { String keycode = KeyMappingMacOS::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); NSMenuItem *menu_item; - int item_count = ((menu == [NSApp mainMenu]) && _has_help_menu()) ? [menu numberOfItems] - 1 : [menu numberOfItems]; - if ((menu == [NSApp mainMenu]) && (p_label == "Help" || p_label == RTR("Help"))) { - p_index = [menu numberOfItems]; - } else if (p_index < 0) { - p_index = item_count; + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + if (p_index < 0) { + p_index = item_start + item_count; } else { - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_index++; - } - p_index = CLAMP(p_index, 0, item_count); + p_index += item_start; + p_index = CLAMP(p_index, item_start, item_start + item_count); } menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; - *r_out = (menu == [NSApp mainMenu]) ? p_index - 1 : p_index; + *r_out = p_index - item_start; return menu_item; } return nullptr; @@ -1070,19 +1100,16 @@ int DisplayServerMacOS::global_menu_add_submenu_item(const String &p_menu_root, return -1; } NSMenuItem *menu_item; - int item_count = ((menu == [NSApp mainMenu]) && _has_help_menu()) ? [menu numberOfItems] - 1 : [menu numberOfItems]; - if ((menu == [NSApp mainMenu]) && (p_label == "Help" || p_label == RTR("Help"))) { - p_index = [menu numberOfItems]; - } else if (p_index < 0) { - p_index = item_count; + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + if (p_index < 0) { + p_index = item_start + item_count; } else { - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_index++; - } - p_index = CLAMP(p_index, 0, item_count); + p_index += item_start; + p_index = CLAMP(p_index, item_start, item_start + item_count); } menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@"" atIndex:p_index]; - out = (menu == [NSApp mainMenu]) ? p_index - 1 : p_index; + out = p_index - item_start; GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = Callable(); @@ -1105,13 +1132,16 @@ int DisplayServerMacOS::global_menu_add_separator(const String &p_menu_root, int if (menu == [NSApp mainMenu]) { // Do not add separators into main menu. return -1; } + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); if (p_index < 0) { - p_index = [menu numberOfItems]; + p_index = item_start + item_count; } else { - p_index = CLAMP(p_index, 0, [menu numberOfItems]); + p_index += item_start; + p_index = CLAMP(p_index, item_start, item_start + item_count); } [menu insertItem:[NSMenuItem separatorItem] atIndex:p_index]; - return p_index; + return p_index - item_start; } return -1; } @@ -1121,11 +1151,8 @@ int DisplayServerMacOS::global_menu_get_item_index_from_text(const String &p_men const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]] - 1; - } else { - return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; - } + int item_start = _get_system_menu_start(menu); + return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]] - item_start; } return -1; @@ -1136,16 +1163,14 @@ int DisplayServerMacOS::global_menu_get_item_index_from_tag(const String &p_menu const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { - for (NSInteger i = (menu == [NSApp mainMenu]) ? 1 : 0; i < [menu numberOfItems]; i++) { + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + for (NSInteger i = item_start; i < item_start + item_count; i++) { const NSMenuItem *menu_item = [menu itemAtIndex:i]; if (menu_item) { const GodotMenuItem *obj = [menu_item representedObject]; if (obj && obj->meta == p_tag) { - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - return i - 1; - } else { - return i; - } + return i - item_start; } } } @@ -1160,10 +1185,10 @@ bool DisplayServerMacOS::global_menu_is_item_checked(const String &p_menu_root, const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, false); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { return ([menu_item state] == NSControlStateValueOn); @@ -1178,10 +1203,10 @@ bool DisplayServerMacOS::global_menu_is_item_checkable(const String &p_menu_root const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, false); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1199,10 +1224,10 @@ bool DisplayServerMacOS::global_menu_is_item_radio_checkable(const String &p_men const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, false); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1220,10 +1245,10 @@ Callable DisplayServerMacOS::global_menu_get_item_callback(const String &p_menu_ const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, Callable()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Callable()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Callable()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1241,10 +1266,10 @@ Callable DisplayServerMacOS::global_menu_get_item_key_callback(const String &p_m const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, Callable()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Callable()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Callable()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1262,10 +1287,10 @@ Variant DisplayServerMacOS::global_menu_get_item_tag(const String &p_menu_root, const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, Variant()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Variant()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Variant()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1283,10 +1308,10 @@ String DisplayServerMacOS::global_menu_get_item_text(const String &p_menu_root, const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, String()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], String()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, String()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { return String::utf8([[menu_item title] UTF8String]); @@ -1301,10 +1326,10 @@ String DisplayServerMacOS::global_menu_get_item_submenu(const String &p_menu_roo const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, String()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], String()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, String()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { NSMenu *sub_menu = [menu_item submenu]; @@ -1322,10 +1347,10 @@ Key DisplayServerMacOS::global_menu_get_item_accelerator(const String &p_menu_ro const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, Key::NONE); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Key::NONE); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Key::NONE); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { String ret = String::utf8([[menu_item keyEquivalent] UTF8String]); @@ -1358,10 +1383,10 @@ bool DisplayServerMacOS::global_menu_is_item_disabled(const String &p_menu_root, const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, false); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { return ![menu_item isEnabled]; @@ -1376,10 +1401,10 @@ bool DisplayServerMacOS::global_menu_is_item_hidden(const String &p_menu_root, i const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, false); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { return [menu_item isHidden]; @@ -1394,10 +1419,10 @@ String DisplayServerMacOS::global_menu_get_item_tooltip(const String &p_menu_roo const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, String()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], String()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, String()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { return String::utf8([[menu_item toolTip] UTF8String]); @@ -1412,10 +1437,10 @@ int DisplayServerMacOS::global_menu_get_item_state(const String &p_menu_root, in const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], 0); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, 0); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1433,10 +1458,10 @@ int DisplayServerMacOS::global_menu_get_item_max_states(const String &p_menu_roo const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], 0); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, 0); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1454,10 +1479,10 @@ Ref<Texture2D> DisplayServerMacOS::global_menu_get_item_icon(const String &p_men const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Ref<Texture2D>()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Ref<Texture2D>()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1477,10 +1502,10 @@ int DisplayServerMacOS::global_menu_get_item_indentation_level(const String &p_m const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], 0); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, 0); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { return [menu_item indentationLevel]; @@ -1495,10 +1520,10 @@ void DisplayServerMacOS::global_menu_set_item_checked(const String &p_menu_root, NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { if (p_checked) { @@ -1516,10 +1541,10 @@ void DisplayServerMacOS::global_menu_set_item_checkable(const String &p_menu_roo NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1535,10 +1560,10 @@ void DisplayServerMacOS::global_menu_set_item_radio_checkable(const String &p_me NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1554,10 +1579,10 @@ void DisplayServerMacOS::global_menu_set_item_callback(const String &p_menu_root NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1573,10 +1598,10 @@ void DisplayServerMacOS::global_menu_set_item_hover_callbacks(const String &p_me NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1592,10 +1617,10 @@ void DisplayServerMacOS::global_menu_set_item_key_callback(const String &p_menu_ NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1611,10 +1636,10 @@ void DisplayServerMacOS::global_menu_set_item_tag(const String &p_menu_root, int NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1630,10 +1655,10 @@ void DisplayServerMacOS::global_menu_set_item_text(const String &p_menu_root, in NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { [menu_item setTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; @@ -1647,10 +1672,10 @@ void DisplayServerMacOS::global_menu_set_item_submenu(const String &p_menu_root, NSMenu *menu = _get_menu_root(p_menu_root); if (menu && p_submenu.is_empty()) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) { @@ -1673,10 +1698,10 @@ void DisplayServerMacOS::global_menu_set_item_submenu(const String &p_menu_root, return; } ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { [menu setSubmenu:sub_menu forItem:menu_item]; @@ -1690,10 +1715,10 @@ void DisplayServerMacOS::global_menu_set_item_accelerator(const String &p_menu_r NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { if (p_keycode == Key::NONE) { @@ -1713,10 +1738,10 @@ void DisplayServerMacOS::global_menu_set_item_disabled(const String &p_menu_root NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { [menu_item setEnabled:(!p_disabled)]; @@ -1730,10 +1755,10 @@ void DisplayServerMacOS::global_menu_set_item_hidden(const String &p_menu_root, NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { [menu_item setHidden:p_hidden]; @@ -1747,10 +1772,10 @@ void DisplayServerMacOS::global_menu_set_item_tooltip(const String &p_menu_root, NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { [menu_item setToolTip:[NSString stringWithUTF8String:p_tooltip.utf8().get_data()]]; @@ -1764,10 +1789,10 @@ void DisplayServerMacOS::global_menu_set_item_state(const String &p_menu_root, i NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1783,10 +1808,10 @@ void DisplayServerMacOS::global_menu_set_item_max_states(const String &p_menu_ro NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1802,10 +1827,10 @@ void DisplayServerMacOS::global_menu_set_item_icon(const String &p_menu_root, in NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1832,10 +1857,10 @@ void DisplayServerMacOS::global_menu_set_item_indentation_level(const String &p_ NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { [menu_item setIndentationLevel:p_level]; @@ -1848,11 +1873,7 @@ int DisplayServerMacOS::global_menu_get_item_count(const String &p_menu_root) co const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - return [menu numberOfItems] - 1; - } else { - return [menu numberOfItems]; - } + return _get_system_menu_count(menu); } else { return 0; } @@ -1864,10 +1885,10 @@ void DisplayServerMacOS::global_menu_remove_item(const String &p_menu_root, int NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) { ERR_PRINT("Can't remove open menu!"); @@ -1886,12 +1907,41 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) { ERR_PRINT("Can't remove open menu!"); return; } - [menu removeAllItems]; - // Restore Apple menu. + + if (menu == apple_menu) { + int start = _get_system_menu_start(apple_menu); + int count = _get_system_menu_count(apple_menu); + for (int i = start + count - 1; i >= start; i--) { + [apple_menu removeItemAtIndex:i]; + } + } else if (menu == window_menu) { + int start = _get_system_menu_start(window_menu); + int count = _get_system_menu_count(window_menu); + for (int i = start + count - 1; i >= start; i--) { + [window_menu removeItemAtIndex:i]; + } + } else if (menu == help_menu) { + int start = _get_system_menu_start(help_menu); + int count = _get_system_menu_count(help_menu); + for (int i = start + count - 1; i >= start; i--) { + [help_menu removeItemAtIndex:i]; + } + } else { + [menu removeAllItems]; + } + + // Restore Apple, Window and Help menu. if (menu == [NSApp mainMenu]) { NSMenuItem *menu_item = [menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; [menu setSubmenu:apple_menu forItem:menu_item]; + + menu_item = [menu addItemWithTitle:@"Window" action:nil keyEquivalent:@""]; + [menu setSubmenu:window_menu forItem:menu_item]; + + menu_item = [menu addItemWithTitle:@"Help" action:nil keyEquivalent:@""]; + [menu setSubmenu:help_menu forItem:menu_item]; } + if (submenu.has(p_menu_root)) { submenu_inv.erase(submenu[p_menu_root].menu); submenu.erase(p_menu_root); @@ -1899,6 +1949,15 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) { } } +Dictionary DisplayServerMacOS::global_menu_get_system_menu_roots() const { + Dictionary out; + out["_dock"] = "@Dock"; + out["_apple"] = "@Apple"; + out["_window"] = "Window"; + out["_help"] = "Help"; + return out; +} + bool DisplayServerMacOS::tts_is_speaking() const { ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return [tts isSpeaking]; @@ -3564,6 +3623,9 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win } break; case WINDOW_FLAG_NO_FOCUS: { wd.no_focus = p_enabled; + + NSWindow *w = wd.window_object; + w.excludedFromWindowsMenu = wd.is_popup || wd.no_focus; } break; case WINDOW_FLAG_MOUSE_PASSTHROUGH: { wd.mpass = p_enabled; @@ -3572,6 +3634,9 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup."); ERR_FAIL_COND_MSG([wd.window_object isVisible] && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened."); wd.is_popup = p_enabled; + + NSWindow *w = wd.window_object; + w.excludedFromWindowsMenu = wd.is_popup || wd.no_focus; } break; default: { } @@ -4488,6 +4553,13 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM [apple_menu addItem:[NSMenuItem separatorItem]]; + menu_item = [apple_menu addItemWithTitle:@"_start_" action:nil keyEquivalent:@""]; + menu_item.hidden = YES; + menu_item.tag = MENU_TAG_START; + menu_item = [apple_menu addItemWithTitle:@"_end_" action:nil keyEquivalent:@""]; + menu_item.hidden = YES; + menu_item.tag = MENU_TAG_END; + NSMenu *services = [[NSMenu alloc] initWithTitle:@""]; menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""]; [apple_menu setSubmenu:services forItem:menu_item]; @@ -4508,10 +4580,41 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname]; [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + window_menu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Window", nil)]; + [window_menu addItemWithTitle:NSLocalizedString(@"Minimize", nil) action:@selector(performMiniaturize:) keyEquivalent:@"m"]; + [window_menu addItemWithTitle:NSLocalizedString(@"Zoom", nil) action:@selector(performZoom:) keyEquivalent:@""]; + [window_menu addItem:[NSMenuItem separatorItem]]; + [window_menu addItemWithTitle:NSLocalizedString(@"Bring All to Front", nil) action:@selector(bringAllToFront:) keyEquivalent:@""]; + [window_menu addItem:[NSMenuItem separatorItem]]; + menu_item = [window_menu addItemWithTitle:@"_start_" action:nil keyEquivalent:@""]; + menu_item.hidden = YES; + menu_item.tag = MENU_TAG_START; + menu_item = [window_menu addItemWithTitle:@"_end_" action:nil keyEquivalent:@""]; + menu_item.hidden = YES; + menu_item.tag = MENU_TAG_END; + + help_menu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Help", nil)]; + menu_item = [help_menu addItemWithTitle:@"_start_" action:nil keyEquivalent:@""]; + menu_item.hidden = YES; + menu_item.tag = MENU_TAG_START; + menu_item = [help_menu addItemWithTitle:@"_end_" action:nil keyEquivalent:@""]; + menu_item.hidden = YES; + menu_item.tag = MENU_TAG_END; + + [NSApp setWindowsMenu:window_menu]; + [NSApp setHelpMenu:help_menu]; + // Add items to the menu bar. NSMenu *main_menu = [NSApp mainMenu]; menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; [main_menu setSubmenu:apple_menu forItem:menu_item]; + + menu_item = [main_menu addItemWithTitle:NSLocalizedString(@"Window", nil) action:nil keyEquivalent:@""]; + [main_menu setSubmenu:window_menu forItem:menu_item]; + + menu_item = [main_menu addItemWithTitle:NSLocalizedString(@"Help", nil) action:nil keyEquivalent:@""]; + [main_menu setSubmenu:help_menu forItem:menu_item]; + [main_menu setAutoenablesItems:NO]; //!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index bf3783966b..08b315fa6c 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -381,6 +381,10 @@ float AnimatedSprite2D::get_playing_speed() const { } void AnimatedSprite2D::set_centered(bool p_center) { + if (centered == p_center) { + return; + } + centered = p_center; queue_redraw(); item_rect_changed(); @@ -391,6 +395,10 @@ bool AnimatedSprite2D::is_centered() const { } void AnimatedSprite2D::set_offset(const Point2 &p_offset) { + if (offset == p_offset) { + return; + } + offset = p_offset; queue_redraw(); item_rect_changed(); @@ -401,6 +409,10 @@ Point2 AnimatedSprite2D::get_offset() const { } void AnimatedSprite2D::set_flip_h(bool p_flip) { + if (hflip == p_flip) { + return; + } + hflip = p_flip; queue_redraw(); } @@ -410,6 +422,10 @@ bool AnimatedSprite2D::is_flipped_h() const { } void AnimatedSprite2D::set_flip_v(bool p_flip) { + if (vflip == p_flip) { + return; + } + vflip = p_flip; queue_redraw(); } diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 2c4bf08f34..b1ff94dda4 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -166,6 +166,21 @@ void Area2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i bool body_in = p_status == PhysicsServer2D::AREA_BODY_ADDED; ObjectID objid = p_instance; + // Exit early if instance is invalid. + if (objid.is_null()) { + // Emit the appropriate signals. + lock_callback(); + locked = true; + if (body_in) { + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, (Node *)nullptr, p_body_shape, p_area_shape); + } else { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, (Node *)nullptr, p_body_shape, p_area_shape); + } + locked = false; + unlock_callback(); + return; + } + Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); @@ -262,6 +277,21 @@ void Area2D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i bool area_in = p_status == PhysicsServer2D::AREA_BODY_ADDED; ObjectID objid = p_instance; + // Exit early if instance is invalid. + if (objid.is_null()) { + // Emit the appropriate signals. + lock_callback(); + locked = true; + if (area_in) { + emit_signal(SceneStringNames::get_singleton()->area_shape_entered, p_area, (Node *)nullptr, p_area_shape, p_self_shape); + } else { + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, p_area, (Node *)nullptr, p_area_shape, p_self_shape); + } + locked = false; + unlock_callback(); + return; + } + Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp index 6d0a2968d7..d3be5b9c65 100644 --- a/scene/2d/sprite_2d.cpp +++ b/scene/2d/sprite_2d.cpp @@ -155,6 +155,10 @@ Ref<Texture2D> Sprite2D::get_texture() const { } void Sprite2D::set_centered(bool p_center) { + if (centered == p_center) { + return; + } + centered = p_center; queue_redraw(); item_rect_changed(); @@ -165,6 +169,10 @@ bool Sprite2D::is_centered() const { } void Sprite2D::set_offset(const Point2 &p_offset) { + if (offset == p_offset) { + return; + } + offset = p_offset; queue_redraw(); item_rect_changed(); @@ -175,6 +183,10 @@ Point2 Sprite2D::get_offset() const { } void Sprite2D::set_flip_h(bool p_flip) { + if (hflip == p_flip) { + return; + } + hflip = p_flip; queue_redraw(); } @@ -184,6 +196,10 @@ bool Sprite2D::is_flipped_h() const { } void Sprite2D::set_flip_v(bool p_flip) { + if (vflip == p_flip) { + return; + } + vflip = p_flip; queue_redraw(); } @@ -193,7 +209,7 @@ bool Sprite2D::is_flipped_v() const { } void Sprite2D::set_region_enabled(bool p_region_enabled) { - if (p_region_enabled == region_enabled) { + if (region_enabled == p_region_enabled) { return; } @@ -223,6 +239,10 @@ Rect2 Sprite2D::get_region_rect() const { } void Sprite2D::set_region_filter_clip_enabled(bool p_region_filter_clip_enabled) { + if (region_filter_clip_enabled == p_region_filter_clip_enabled) { + return; + } + region_filter_clip_enabled = p_region_filter_clip_enabled; queue_redraw(); } @@ -234,12 +254,12 @@ bool Sprite2D::is_region_filter_clip_enabled() const { void Sprite2D::set_frame(int p_frame) { ERR_FAIL_INDEX(p_frame, vframes * hframes); - if (frame != p_frame) { - item_rect_changed(); + if (frame == p_frame) { + return; } frame = p_frame; - + item_rect_changed(); emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -260,6 +280,11 @@ Vector2i Sprite2D::get_frame_coords() const { void Sprite2D::set_vframes(int p_amount) { ERR_FAIL_COND_MSG(p_amount < 1, "Amount of vframes cannot be smaller than 1."); + + if (vframes == p_amount) { + return; + } + vframes = p_amount; if (frame >= vframes * hframes) { frame = 0; @@ -275,6 +300,11 @@ int Sprite2D::get_vframes() const { void Sprite2D::set_hframes(int p_amount) { ERR_FAIL_COND_MSG(p_amount < 1, "Amount of hframes cannot be smaller than 1."); + + if (hframes == p_amount) { + return; + } + if (vframes > 1) { // Adjust the frame to fit new sheet dimensions. int original_column = frame % hframes; diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index 585bce09fb..014c33cad0 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -223,6 +223,21 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i bool body_in = p_status == PhysicsServer3D::AREA_BODY_ADDED; ObjectID objid = p_instance; + // Exit early if instance is invalid. + if (objid.is_null()) { + lock_callback(); + locked = true; + // Emit the appropriate signals. + if (body_in) { + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, (Node *)nullptr, p_body_shape, p_area_shape); + } else { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, (Node *)nullptr, p_body_shape, p_area_shape); + } + locked = false; + unlock_callback(); + return; + } + Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); @@ -254,7 +269,7 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i E->value.shapes.insert(ShapePair(p_body_shape, p_area_shape)); } - if (E->value.in_tree) { + if (!node || E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_area_shape); } @@ -276,7 +291,7 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i } } } - if (node && in_tree) { + if (!node || in_tree) { emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, obj, p_body_shape, p_area_shape); } } @@ -414,6 +429,21 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i bool area_in = p_status == PhysicsServer3D::AREA_BODY_ADDED; ObjectID objid = p_instance; + // Exit if instance is invalid. + if (objid.is_null()) { + lock_callback(); + locked = true; + // Emit the appropriate signals. + if (area_in) { + emit_signal(SceneStringNames::get_singleton()->area_shape_entered, p_area, (Node *)nullptr, p_area_shape, p_self_shape); + } else { + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, p_area, (Node *)nullptr, p_area_shape, p_self_shape); + } + locked = false; + unlock_callback(); + return; + } + Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index ffd328c37e..b8b0d31d45 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -286,6 +286,10 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, } void SpriteBase3D::set_centered(bool p_center) { + if (centered == p_center) { + return; + } + centered = p_center; _queue_redraw(); } @@ -295,6 +299,10 @@ bool SpriteBase3D::is_centered() const { } void SpriteBase3D::set_offset(const Point2 &p_offset) { + if (offset == p_offset) { + return; + } + offset = p_offset; _queue_redraw(); } @@ -304,6 +312,10 @@ Point2 SpriteBase3D::get_offset() const { } void SpriteBase3D::set_flip_h(bool p_flip) { + if (hflip == p_flip) { + return; + } + hflip = p_flip; _queue_redraw(); } @@ -313,6 +325,10 @@ bool SpriteBase3D::is_flipped_h() const { } void SpriteBase3D::set_flip_v(bool p_flip) { + if (vflip == p_flip) { + return; + } + vflip = p_flip; _queue_redraw(); } @@ -322,6 +338,10 @@ bool SpriteBase3D::is_flipped_v() const { } void SpriteBase3D::set_modulate(const Color &p_color) { + if (modulate == p_color) { + return; + } + modulate = p_color; _propagate_color_changed(); _queue_redraw(); @@ -333,6 +353,11 @@ Color SpriteBase3D::get_modulate() const { void SpriteBase3D::set_render_priority(int p_priority) { ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX); + + if (render_priority == p_priority) { + return; + } + render_priority = p_priority; _queue_redraw(); } @@ -342,6 +367,10 @@ int SpriteBase3D::get_render_priority() const { } void SpriteBase3D::set_pixel_size(real_t p_amount) { + if (pixel_size == p_amount) { + return; + } + pixel_size = p_amount; _queue_redraw(); } @@ -352,6 +381,11 @@ real_t SpriteBase3D::get_pixel_size() const { void SpriteBase3D::set_axis(Vector3::Axis p_axis) { ERR_FAIL_INDEX(p_axis, 3); + + if (axis == p_axis) { + return; + } + axis = p_axis; _queue_redraw(); } @@ -445,6 +479,11 @@ Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const { void SpriteBase3D::set_draw_flag(DrawFlags p_flag, bool p_enable) { ERR_FAIL_INDEX(p_flag, FLAG_MAX); + + if (flags[p_flag] == p_enable) { + return; + } + flags[p_flag] = p_enable; _queue_redraw(); } @@ -456,6 +495,11 @@ bool SpriteBase3D::get_draw_flag(DrawFlags p_flag) const { void SpriteBase3D::set_alpha_cut_mode(AlphaCutMode p_mode) { ERR_FAIL_INDEX(p_mode, ALPHA_CUT_MAX); + + if (alpha_cut == p_mode) { + return; + } + alpha_cut = p_mode; _queue_redraw(); } @@ -465,10 +509,12 @@ SpriteBase3D::AlphaCutMode SpriteBase3D::get_alpha_cut_mode() const { } void SpriteBase3D::set_alpha_hash_scale(float p_hash_scale) { - if (alpha_hash_scale != p_hash_scale) { - alpha_hash_scale = p_hash_scale; - _queue_redraw(); + if (alpha_hash_scale == p_hash_scale) { + return; } + + alpha_hash_scale = p_hash_scale; + _queue_redraw(); } float SpriteBase3D::get_alpha_hash_scale() const { @@ -476,10 +522,12 @@ float SpriteBase3D::get_alpha_hash_scale() const { } void SpriteBase3D::set_alpha_scissor_threshold(float p_threshold) { - if (alpha_scissor_threshold != p_threshold) { - alpha_scissor_threshold = p_threshold; - _queue_redraw(); + if (alpha_scissor_threshold == p_threshold) { + return; } + + alpha_scissor_threshold = p_threshold; + _queue_redraw(); } float SpriteBase3D::get_alpha_scissor_threshold() const { @@ -487,10 +535,12 @@ float SpriteBase3D::get_alpha_scissor_threshold() const { } void SpriteBase3D::set_alpha_antialiasing(BaseMaterial3D::AlphaAntiAliasing p_alpha_aa) { - if (alpha_antialiasing_mode != p_alpha_aa) { - alpha_antialiasing_mode = p_alpha_aa; - _queue_redraw(); + if (alpha_antialiasing_mode == p_alpha_aa) { + return; } + + alpha_antialiasing_mode = p_alpha_aa; + _queue_redraw(); } BaseMaterial3D::AlphaAntiAliasing SpriteBase3D::get_alpha_antialiasing() const { @@ -498,10 +548,12 @@ BaseMaterial3D::AlphaAntiAliasing SpriteBase3D::get_alpha_antialiasing() const { } void SpriteBase3D::set_alpha_antialiasing_edge(float p_edge) { - if (alpha_antialiasing_edge != p_edge) { - alpha_antialiasing_edge = p_edge; - _queue_redraw(); + if (alpha_antialiasing_edge == p_edge) { + return; } + + alpha_antialiasing_edge = p_edge; + _queue_redraw(); } float SpriteBase3D::get_alpha_antialiasing_edge() const { @@ -510,6 +562,11 @@ float SpriteBase3D::get_alpha_antialiasing_edge() const { void SpriteBase3D::set_billboard_mode(StandardMaterial3D::BillboardMode p_mode) { ERR_FAIL_INDEX(p_mode, 3); // Cannot use BILLBOARD_PARTICLES. + + if (billboard_mode == p_mode) { + return; + } + billboard_mode = p_mode; _queue_redraw(); } @@ -519,10 +576,12 @@ StandardMaterial3D::BillboardMode SpriteBase3D::get_billboard_mode() const { } void SpriteBase3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) { - if (texture_filter != p_filter) { - texture_filter = p_filter; - _queue_redraw(); + if (texture_filter == p_filter) { + return; } + + texture_filter = p_filter; + _queue_redraw(); } StandardMaterial3D::TextureFilter SpriteBase3D::get_texture_filter() const { @@ -767,9 +826,12 @@ bool Sprite3D::is_region_enabled() const { } void Sprite3D::set_region_rect(const Rect2 &p_region_rect) { - bool changed = region_rect != p_region_rect; + if (region_rect == p_region_rect) { + return; + } + region_rect = p_region_rect; - if (region && changed) { + if (region) { _queue_redraw(); } } @@ -781,10 +843,12 @@ Rect2 Sprite3D::get_region_rect() const { void Sprite3D::set_frame(int p_frame) { ERR_FAIL_INDEX(p_frame, int64_t(vframes) * hframes); - frame = p_frame; + if (frame == p_frame) { + return; + } + frame = p_frame; _queue_redraw(); - emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -805,6 +869,11 @@ Vector2i Sprite3D::get_frame_coords() const { void Sprite3D::set_vframes(int p_amount) { ERR_FAIL_COND_MSG(p_amount < 1, "Amount of vframes cannot be smaller than 1."); + + if (vframes == p_amount) { + return; + } + vframes = p_amount; if (frame >= vframes * hframes) { frame = 0; @@ -819,6 +888,11 @@ int Sprite3D::get_vframes() const { void Sprite3D::set_hframes(int p_amount) { ERR_FAIL_COND_MSG(p_amount < 1, "Amount of hframes cannot be smaller than 1."); + + if (hframes == p_amount) { + return; + } + if (vframes > 1) { // Adjust the frame to fit new sheet dimensions. int original_column = frame % hframes; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index d31806fe97..1d5c1c0ade 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -556,16 +556,17 @@ void ColorPicker::_html_submitted(const String &p_html) { return; } - const Color previous_color = color; - color = Color::from_string(p_html.strip_edges(), previous_color); + Color new_color = Color::from_string(p_html.strip_edges(), color); if (!is_editing_alpha()) { - color.a = previous_color.a; + new_color.a = color.a; } - if (color == previous_color) { + if (new_color.to_argb32() == color.to_argb32()) { return; } + color = new_color; + if (!is_inside_tree()) { return; } diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 7418ba7333..7fa2653ed9 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -249,11 +249,13 @@ String MenuBar::bind_global_menu() { Vector<PopupMenu *> popups = _get_popups(); for (int i = 0; i < menu_cache.size(); i++) { String submenu_name = popups[i]->bind_global_menu(); - int index = ds->global_menu_add_submenu_item("_main", menu_cache[i].name, submenu_name, global_start_idx + i); - ds->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(i)); - ds->global_menu_set_item_hidden("_main", index, menu_cache[i].hidden); - ds->global_menu_set_item_disabled("_main", index, menu_cache[i].disabled); - ds->global_menu_set_item_tooltip("_main", index, menu_cache[i].tooltip); + if (!popups[i]->is_system_menu()) { + int index = ds->global_menu_add_submenu_item("_main", menu_cache[i].name, submenu_name, global_start_idx + i); + ds->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(i)); + ds->global_menu_set_item_hidden("_main", index, menu_cache[i].hidden); + ds->global_menu_set_item_disabled("_main", index, menu_cache[i].disabled); + ds->global_menu_set_item_tooltip("_main", index, menu_cache[i].tooltip); + } } return global_menu_name; @@ -268,8 +270,10 @@ void MenuBar::unbind_global_menu() { int global_start = _find_global_start_index(); Vector<PopupMenu *> popups = _get_popups(); for (int i = menu_cache.size() - 1; i >= 0; i--) { - popups[i]->unbind_global_menu(); - ds->global_menu_remove_item("_main", global_start + i); + if (!popups[i]->is_system_menu()) { + popups[i]->unbind_global_menu(); + ds->global_menu_remove_item("_main", global_start + i); + } } global_menu_name = String(); @@ -558,8 +562,10 @@ void MenuBar::add_child_notify(Node *p_child) { if (!global_menu_name.is_empty()) { String submenu_name = pm->bind_global_menu(); - int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, _find_global_start_index() + menu_cache.size() - 1); - DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(menu_cache.size() - 1)); + if (!pm->is_system_menu()) { + int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, _find_global_start_index() + menu_cache.size() - 1); + DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(menu_cache.size() - 1)); + } } update_minimum_size(); } @@ -587,14 +593,16 @@ void MenuBar::move_child_notify(Node *p_child) { menu_cache.insert(new_idx, menu); if (!global_menu_name.is_empty()) { - int global_start = _find_global_start_index(); - if (old_idx != -1) { - DisplayServer::get_singleton()->global_menu_remove_item("_main", global_start + old_idx); - } - if (new_idx != -1) { - String submenu_name = pm->bind_global_menu(); - int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, global_start + new_idx); - DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(new_idx)); + if (!pm->is_system_menu()) { + int global_start = _find_global_start_index(); + if (old_idx != -1) { + DisplayServer::get_singleton()->global_menu_remove_item("_main", global_start + old_idx); + } + if (new_idx != -1) { + String submenu_name = pm->bind_global_menu(); + int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, global_start + new_idx); + DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(new_idx)); + } } } } @@ -612,8 +620,10 @@ void MenuBar::remove_child_notify(Node *p_child) { menu_cache.remove_at(idx); if (!global_menu_name.is_empty()) { - pm->unbind_global_menu(); - DisplayServer::get_singleton()->global_menu_remove_item("_main", _find_global_start_index() + idx); + if (!pm->is_system_menu()) { + pm->unbind_global_menu(); + DisplayServer::get_singleton()->global_menu_remove_item("_main", _find_global_start_index() + idx); + } } p_child->remove_meta("_menu_name"); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index d6b8dd0202..605a3fe397 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -40,6 +40,8 @@ #include "scene/gui/menu_bar.h" #include "scene/theme/theme_db.h" +HashMap<String, PopupMenu *> PopupMenu::system_menus; + String PopupMenu::bind_global_menu() { #ifdef TOOLS_ENABLED if (is_part_of_edited_scene()) { @@ -54,8 +56,20 @@ String PopupMenu::bind_global_menu() { return global_menu_name; // Already bound; } - DisplayServer *ds = DisplayServer::get_singleton(); global_menu_name = "__PopupMenu#" + itos(get_instance_id()); + if (system_menu_name.length() > 0) { + if (system_menus.has(system_menu_name)) { + WARN_PRINT(vformat("Attempting to bind PopupMenu to the special menu %s, but another menu is already bound to it. This menu: %s, current menu: %s", system_menu_name, this->get_description(), system_menus[system_menu_name]->get_description())); + } else { + const Dictionary &supported_special_names = DisplayServer::get_singleton()->global_menu_get_system_menu_roots(); + if (supported_special_names.has(system_menu_name)) { + system_menus[system_menu_name] = this; + global_menu_name = system_menu_name; + } + } + } + + DisplayServer *ds = DisplayServer::get_singleton(); ds->global_menu_set_popup_callbacks(global_menu_name, callable_mp(this, &PopupMenu::_about_to_popup), callable_mp(this, &PopupMenu::_about_to_close)); for (int i = 0; i < items.size(); i++) { Item &item = items.write[i]; @@ -105,6 +119,10 @@ void PopupMenu::unbind_global_menu() { return; } + if (global_menu_name == system_menu_name && system_menus[system_menu_name] == this) { + system_menus.erase(system_menu_name); + } + for (int i = 0; i < items.size(); i++) { Item &item = items.write[i]; if (!item.submenu.is_empty()) { @@ -120,6 +138,24 @@ void PopupMenu::unbind_global_menu() { global_menu_name = String(); } +bool PopupMenu::is_system_menu() const { + return (global_menu_name == system_menu_name) && (system_menu_name.length() > 0); +} + +void PopupMenu::set_system_menu_root(const String &p_special) { + if (is_inside_tree() && system_menu_name.length() > 0) { + unbind_global_menu(); + } + system_menu_name = p_special; + if (is_inside_tree() && system_menu_name.length() > 0) { + bind_global_menu(); + } +} + +String PopupMenu::get_system_menu_root() const { + return system_menu_name; +} + String PopupMenu::_get_accel_text(const Item &p_item) const { if (p_item.shortcut.is_valid()) { return p_item.shortcut->get_as_text(); @@ -947,6 +983,15 @@ void PopupMenu::_notification(int p_what) { if (!is_embedded()) { set_flag(FLAG_NO_FOCUS, true); } + if (system_menu_name.length() > 0) { + bind_global_menu(); + } + } break; + + case NOTIFICATION_EXIT_TREE: { + if (system_menu_name.length() > 0) { + unbind_global_menu(); + } } break; case NOTIFICATION_THEME_CHANGED: @@ -2716,11 +2761,16 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &PopupMenu::set_allow_search); ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search); + ClassDB::bind_method(D_METHOD("is_system_menu"), &PopupMenu::is_system_menu); + ClassDB::bind_method(D_METHOD("set_system_menu_root", "special"), &PopupMenu::set_system_menu_root); + ClassDB::bind_method(D_METHOD("get_system_menu_root"), &PopupMenu::get_system_menu_root); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "submenu_popup_delay", PROPERTY_HINT_NONE, "suffix:s"), "set_submenu_popup_delay", "get_submenu_popup_delay"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "system_menu_root", PROPERTY_HINT_ENUM, "Dock (macOS):_dock,Apple Menu(macOS):_apple,Window Menu(macOS):_window,Help Menu(macOS):_help"), "set_system_menu_root", "get_system_menu_root"); ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "item_"); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 5d5f4a8322..4703686c51 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -40,6 +40,8 @@ class PopupMenu : public Popup { GDCLASS(PopupMenu, Popup); + static HashMap<String, PopupMenu *> system_menus; + struct Item { Ref<Texture2D> icon; int icon_max_width = 0; @@ -90,6 +92,7 @@ class PopupMenu : public Popup { }; String global_menu_name; + String system_menu_name; bool close_allowed = false; bool activated_by_keyboard = false; @@ -218,6 +221,9 @@ public: String bind_global_menu(); void unbind_global_menu(); + bool is_system_menu() const; + void set_system_menu_root(const String &p_special); + String get_system_menu_root() const; void add_item(const String &p_label, int p_id = -1, Key p_accel = Key::NONE); void add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id = -1, Key p_accel = Key::NONE); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 8cb7614dfd..89ec5636ab 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -3518,6 +3518,11 @@ Control *Viewport::gui_get_focus_owner() const { return gui.key_focus; } +Control *Viewport::gui_get_hovered_control() const { + ERR_READ_THREAD_GUARD_V(nullptr); + return gui.mouse_over; +} + void Viewport::set_msaa_2d(MSAA p_msaa) { ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_msaa, MSAA_MAX); @@ -4557,6 +4562,7 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("gui_release_focus"), &Viewport::gui_release_focus); ClassDB::bind_method(D_METHOD("gui_get_focus_owner"), &Viewport::gui_get_focus_owner); + ClassDB::bind_method(D_METHOD("gui_get_hovered_control"), &Viewport::gui_get_hovered_control); ClassDB::bind_method(D_METHOD("set_disable_input", "disable"), &Viewport::set_disable_input); ClassDB::bind_method(D_METHOD("is_input_disabled"), &Viewport::is_input_disabled); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 6efa98ece8..43a89c8a0b 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -610,6 +610,7 @@ public: void gui_release_focus(); Control *gui_get_focus_owner() const; + Control *gui_get_hovered_control() const; PackedStringArray get_configuration_warnings() const override; diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 6459cc7462..bb28bc0eb8 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -267,6 +267,11 @@ void DisplayServer::global_menu_clear(const String &p_menu_root) { WARN_PRINT("Global menus not supported by this display server."); } +Dictionary DisplayServer::global_menu_get_system_menu_roots() const { + WARN_PRINT("Global menus not supported by this display server."); + return Dictionary(); +} + bool DisplayServer::tts_is_speaking() const { WARN_PRINT("TTS is not supported by this display server."); return false; @@ -652,6 +657,8 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("global_menu_remove_item", "menu_root", "idx"), &DisplayServer::global_menu_remove_item); ClassDB::bind_method(D_METHOD("global_menu_clear", "menu_root"), &DisplayServer::global_menu_clear); + ClassDB::bind_method(D_METHOD("global_menu_get_system_menu_roots"), &DisplayServer::global_menu_get_system_menu_roots); + ClassDB::bind_method(D_METHOD("tts_is_speaking"), &DisplayServer::tts_is_speaking); ClassDB::bind_method(D_METHOD("tts_is_paused"), &DisplayServer::tts_is_paused); ClassDB::bind_method(D_METHOD("tts_get_voices"), &DisplayServer::tts_get_voices); diff --git a/servers/display_server.h b/servers/display_server.h index d2e112d224..4450677f71 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -185,6 +185,8 @@ public: virtual void global_menu_remove_item(const String &p_menu_root, int p_idx); virtual void global_menu_clear(const String &p_menu_root); + virtual Dictionary global_menu_get_system_menu_roots() const; + struct TTSUtterance { String text; String voice; diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 004fe26b85..e17fa0ce90 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -1860,7 +1860,7 @@ void fragment_shader(in SceneData scene_data) { pssm_coord /= pssm_coord.w; - shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * blur_factor, pssm_coord); + shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * float(directional_lights.data[i].blend_splits)), pssm_coord); if (directional_lights.data[i].blend_splits) { float pssm_blend; @@ -1870,21 +1870,21 @@ void fragment_shader(in SceneData scene_data) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 1) pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); - pssm_blend = smoothstep(0.0, directional_lights.data[i].shadow_split_offsets.x, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x - directional_lights.data[i].shadow_split_offsets.x * 0.1, directional_lights.data[i].shadow_split_offsets.x, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 2) pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x, directional_lights.data[i].shadow_split_offsets.y, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y - directional_lights.data[i].shadow_split_offsets.y * 0.1, directional_lights.data[i].shadow_split_offsets.y, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 3) pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y, directional_lights.data[i].shadow_split_offsets.z, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.z - directional_lights.data[i].shadow_split_offsets.z * 0.1, directional_lights.data[i].shadow_split_offsets.z, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; } else { @@ -1894,7 +1894,7 @@ void fragment_shader(in SceneData scene_data) { pssm_coord /= pssm_coord.w; - float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * blur_factor2, pssm_coord); + float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * float(directional_lights.data[i].blend_splits)), pssm_coord); shadow = mix(shadow, shadow2, pssm_blend); } } diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index d620e21cb1..03737e087c 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -1506,7 +1506,7 @@ void main() { pssm_coord /= pssm_coord.w; - shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * blur_factor, pssm_coord); + shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * float(directional_lights.data[i].blend_splits)), pssm_coord); if (directional_lights.data[i].blend_splits) { float pssm_blend; @@ -1516,21 +1516,21 @@ void main() { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 1) pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); - pssm_blend = smoothstep(0.0, directional_lights.data[i].shadow_split_offsets.x, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x - directional_lights.data[i].shadow_split_offsets.x * 0.1, directional_lights.data[i].shadow_split_offsets.x, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 2) pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x, directional_lights.data[i].shadow_split_offsets.y, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y - directional_lights.data[i].shadow_split_offsets.y * 0.1, directional_lights.data[i].shadow_split_offsets.y, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 3) pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y, directional_lights.data[i].shadow_split_offsets.z, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.z - directional_lights.data[i].shadow_split_offsets.z * 0.1, directional_lights.data[i].shadow_split_offsets.z, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; } else { @@ -1540,7 +1540,7 @@ void main() { pssm_coord /= pssm_coord.w; - float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * blur_factor2, pssm_coord); + float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * float(directional_lights.data[i].blend_splits)), pssm_coord); shadow = mix(shadow, shadow2, pssm_blend); } |
