diff --git a/godot-codegen/src/generator/enums.rs b/godot-codegen/src/generator/enums.rs index 2238de5da..3a5f1b516 100644 --- a/godot-codegen/src/generator/enums.rs +++ b/godot-codegen/src/generator/enums.rs @@ -485,7 +485,12 @@ fn make_enum_as_str(enum_: &Enum) -> TokenStream { /// Creates implementations for bitwise operators for the given enum. /// -/// Currently, this is just [`BitOr`](std::ops::BitOr) for bitfields but that could be expanded in the future. +/// Currently, for bitfields this is the following: +/// - [`BitOr`][std::ops::BitOr] +/// - [`BitOrAssign`][std::ops::BitOrAssign] +/// +/// While it would be possible to expand these with additional bitwise operations such as `BitAnd` and `Not`, the preferred way to manipulate +/// flags is to use the `with` and `without` methods rather than using the bitwise operators themselves. fn make_enum_bitwise_operators(enum_: &Enum, enum_bitmask: Option<&RustTy>) -> TokenStream { let name = &enum_.name; diff --git a/godot-core/src/obj/traits.rs b/godot-core/src/obj/traits.rs index 20c0dd63b..aadbc0a7b 100644 --- a/godot-core/src/obj/traits.rs +++ b/godot-core/src/obj/traits.rs @@ -285,6 +285,16 @@ pub trait EngineBitfield: Copy + 'static { /// } /// ``` fn all_constants() -> &'static [EnumConstant]; + + /// Returns the flag(s) from `self` combined with the flag(s) from `add_flags` arg. + fn with(self, add_flags: Self) -> Self { + Self::from_ord(self.ord() | add_flags.ord()) + } + + /// Returns the flag(s) from `self`, except for any that were present in the `remove_flags` arg. + fn without(self, remove_flags: Self) -> Self { + Self::from_ord(self.ord() & !remove_flags.ord()) + } } /// Trait for enums that can be used as indices in arrays. diff --git a/itest/rust/src/object_tests/bitfield_ops_test.rs b/itest/rust/src/object_tests/bitfield_ops_test.rs new file mode 100644 index 000000000..b6eeabc07 --- /dev/null +++ b/itest/rust/src/object_tests/bitfield_ops_test.rs @@ -0,0 +1,46 @@ +/* + * Copyright (c) godot-rust; Bromeon and contributors. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use godot::classes::node::DuplicateFlags; +use godot::obj::EngineBitfield; + +use crate::framework::itest; + +const SIGNALS: DuplicateFlags = DuplicateFlags::SIGNALS; // 1 +const GROUPS: DuplicateFlags = DuplicateFlags::GROUPS; // 2 +const SCRIPTS: DuplicateFlags = DuplicateFlags::SCRIPTS; // 4 + +#[itest] +fn bitfield_ops_with() { + let no_flags = DuplicateFlags::from_ord(0); + + assert_eq!(no_flags.with(GROUPS).ord(), 2); + assert_eq!(GROUPS.with(no_flags).ord(), 2); + assert_eq!(GROUPS.with(GROUPS).ord(), 2); + + assert_eq!(GROUPS.with(SIGNALS).ord(), 1 | 2); + assert_eq!(GROUPS.with(GROUPS.with(SIGNALS)).ord(), 1 | 2); + assert_eq!(GROUPS.with(GROUPS).with(SIGNALS).ord(), 1 | 2); + + assert_eq!(GROUPS.with(SIGNALS).with(SCRIPTS).ord(), 1 | 2 | 4); +} + +#[itest] +fn bitfield_ops_without() { + let no_flags = DuplicateFlags::from_ord(0); + + assert_eq!(no_flags.without(no_flags).ord(), 0); + assert_eq!(GROUPS.without(GROUPS).ord(), 0); + + assert_eq!(GROUPS.without(SIGNALS).ord(), 2); + assert_eq!(SIGNALS.with(GROUPS).without(SIGNALS).ord(), 2); + assert_eq!(SIGNALS.with(GROUPS).without(SIGNALS.with(SCRIPTS)).ord(), 2); + + let all = GROUPS.with(SIGNALS).with(SCRIPTS); + assert_eq!(all.without(SIGNALS).ord(), 2 | 4); + assert_eq!(all.without(SIGNALS).without(SCRIPTS).ord(), 2); +} diff --git a/itest/rust/src/object_tests/mod.rs b/itest/rust/src/object_tests/mod.rs index 2e9fdf43f..1af2afe8b 100644 --- a/itest/rust/src/object_tests/mod.rs +++ b/itest/rust/src/object_tests/mod.rs @@ -6,6 +6,7 @@ */ mod base_test; +mod bitfield_ops_test; mod call_deferred_test; mod class_id_test; mod class_rename_test;