Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions godot-codegen/src/generator/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use std::collections::HashSet;

use heck::ToPascalCase;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};

Expand Down Expand Up @@ -45,6 +46,7 @@ pub fn make_enum_definition_with(
// Things needed for the type definition
let derives = enum_.derives();
let enum_doc = make_enum_doc(enum_);
let enum_hint_string = make_enum_hint_string(enum_);
let name = &enum_.name;

// Values
Expand Down Expand Up @@ -105,9 +107,28 @@ pub fn make_enum_definition_with(

// Trait implementations.
let engine_trait_impl = make_enum_engine_trait_impl(enum_, enum_bitmask.as_ref());
let property_hint = if enum_.is_bitfield {
quote! { crate::global::PropertyHint::FLAGS }
} else {
quote! { crate::global::PropertyHint::ENUM }
};
let index_enum_impl = make_enum_index_impl(enum_);
let bitwise_impls = make_enum_bitwise_operators(enum_, enum_bitmask.as_ref());

let var_trait_set_property = if enum_.is_exhaustive {
quote! {
fn set_property(&mut self, value: Self::Via) {
*self = <Self as #engine_trait>::from_ord(value);
}
}
} else {
quote! {
fn set_property(&mut self, value: Self::Via) {
self.ord = value;
}
}
};

quote! {
#engine_trait_impl
#index_enum_impl
Expand All @@ -131,6 +152,23 @@ pub fn make_enum_definition_with(
.ok_or_else(|| crate::meta::error::FromGodotError::InvalidEnum.into_error(via))
}
}

impl crate::registry::property::Var for #name {
fn get_property(&self) -> Self::Via{
<Self as #engine_trait>::ord(*self)
}

#var_trait_set_property

fn var_hint() -> crate::meta::PropertyHintInfo{
crate::meta::PropertyHintInfo{
hint: #property_hint,
hint_string: crate::builtin::GString::from(#enum_hint_string),
}
}
}

impl crate::registry::property::Export for #name {}
}
});

Expand Down Expand Up @@ -481,6 +519,17 @@ fn make_enum_doc(enum_: &Enum) -> Vec<String> {

docs
}
/// Returns the hint string for the given enum.
///
/// e.g.: "Left,Center,Right,Fill"
fn make_enum_hint_string(enum_: &Enum) -> String {
enum_
.enumerators
.iter()
.map(|enumerator| enumerator.name.to_string().to_pascal_case())
.collect::<Vec<String>>()
.join(",")
}

/// Creates a definition for `enumerator` of the type `enum_type`.
///
Expand Down
34 changes: 17 additions & 17 deletions godot-core/src/registry/property/phantom_var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;

use crate::meta::{ClassId, GodotConvert, GodotType, PropertyHintInfo};
use crate::meta::{ClassId, GodotConvert, PropertyHintInfo};
use crate::registry::property::{Export, Var};

/// A zero-sized type for creating a property without a backing field, accessible only through custom getter/setter functions.
Expand Down Expand Up @@ -55,15 +55,15 @@ use crate::registry::property::{Export, Var};
/// This field can now be accessed from GDScript as `banner.text`.
// Bounds for T are somewhat un-idiomatically directly on the type, rather than impls.
// This improves error messages in IDEs when using the type as a field.
pub struct PhantomVar<T: GodotType + Var>(PhantomData<T>);
pub struct PhantomVar<T: GodotConvert + Var>(PhantomData<T>);

impl<T: GodotType + Var> GodotConvert for PhantomVar<T> {
type Via = T;
impl<T: GodotConvert + Var> GodotConvert for PhantomVar<T> {
type Via = <T as GodotConvert>::Via;
}

// `PhantomVar` supports only part of `Var`, but it has to implement it, otherwise we cannot implement `Export` either.
// The `GodotClass` derive macro should ensure that the `Var` implementation is not used.
impl<T: GodotType + Var> Var for PhantomVar<T> {
impl<T: GodotConvert + Var> Var for PhantomVar<T> {
fn get_property(&self) -> Self::Via {
unreachable!("code generated by GodotClass should call the custom getter")
}
Expand All @@ -78,7 +78,7 @@ impl<T: GodotType + Var> Var for PhantomVar<T> {
}

// Reuse values from `T`, if any.
impl<T: GodotType + Var + Export> Export for PhantomVar<T> {
impl<T: GodotConvert + Var + Export> Export for PhantomVar<T> {
fn export_hint() -> PropertyHintInfo {
<T as Export>::export_hint()
}
Expand All @@ -88,7 +88,7 @@ impl<T: GodotType + Var + Export> Export for PhantomVar<T> {
}
}

impl<T: GodotType + Var> Default for PhantomVar<T> {
impl<T: GodotConvert + Var> Default for PhantomVar<T> {
fn default() -> Self {
Self(Default::default())
}
Expand All @@ -97,49 +97,49 @@ impl<T: GodotType + Var> Default for PhantomVar<T> {
// Like `PhantomData` from the Rust standard library, `PhantomVar` implements many common traits like `Eq` and `Hash`
// to allow these traits to be derived on containing structs as well.

impl<T: GodotType + Var> Clone for PhantomVar<T> {
impl<T: GodotConvert + Var> Clone for PhantomVar<T> {
fn clone(&self) -> Self {
*self
}
}

impl<T: GodotType + Var> Copy for PhantomVar<T> {}
impl<T: GodotConvert + Var> Copy for PhantomVar<T> {}

impl<T: GodotType + Var> fmt::Debug for PhantomVar<T> {
impl<T: GodotConvert + Var> fmt::Debug for PhantomVar<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("PhantomVar").finish()
}
}

impl<T: GodotType + Var> PartialEq for PhantomVar<T> {
impl<T: GodotConvert + Var> PartialEq for PhantomVar<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}

impl<T: GodotType + Var> Eq for PhantomVar<T> {}
impl<T: GodotConvert + Var> Eq for PhantomVar<T> {}

impl<T: GodotType + Var> PartialOrd for PhantomVar<T> {
impl<T: GodotConvert + Var> PartialOrd for PhantomVar<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl<T: GodotType + Var> Ord for PhantomVar<T> {
impl<T: GodotConvert + Var> Ord for PhantomVar<T> {
fn cmp(&self, _other: &Self) -> Ordering {
Ordering::Equal
}
}

impl<T: GodotType + Var> Hash for PhantomVar<T> {
impl<T: GodotConvert + Var> Hash for PhantomVar<T> {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}

// SAFETY: This type contains no data.
unsafe impl<T: GodotType + Var> Send for PhantomVar<T> {}
unsafe impl<T: GodotConvert + Var> Send for PhantomVar<T> {}

// SAFETY: This type contains no data.
unsafe impl<T: GodotType + Var> Sync for PhantomVar<T> {}
unsafe impl<T: GodotConvert + Var> Sync for PhantomVar<T> {}

/// This type exists only as a place to add `compile_fail` doctests for `PhantomVar`, which do not need to be in the public documentation.
///
Expand Down
12 changes: 12 additions & 0 deletions itest/godot/ManualFfiTests.gd
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,18 @@ func test_phantom_var_writing_read_only():
obj.read_only = 1
assert_fail("HasPhantomVar.read_only should not be writable")

func test_phantom_var_enum():
var obj := HasPhantomVar.new()

assert_eq(obj.read_write_engine_enum, VerticalAlignment.VERTICAL_ALIGNMENT_CENTER)
assert_eq(obj.read_write_bit_enum, KeyModifierMask.KEY_MASK_ALT | KeyModifierMask.KEY_MASK_CTRL)

obj.read_write_engine_enum = VerticalAlignment.VERTICAL_ALIGNMENT_TOP
obj.read_write_bit_enum = KeyModifierMask.KEY_MASK_ALT | KeyModifierMask.KEY_MASK_SHIFT

assert_eq(obj.read_write_engine_enum, VerticalAlignment.VERTICAL_ALIGNMENT_TOP)
assert_eq(obj.read_write_bit_enum, KeyModifierMask.KEY_MASK_ALT | KeyModifierMask.KEY_MASK_SHIFT)

func test_option_export():
var obj := OptionExportFfiTest.new()

Expand Down
32 changes: 32 additions & 0 deletions itest/rust/src/object_tests/phantom_var_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,19 @@ struct HasPhantomVar {
#[var(get = get_read_write, set = set_read_write)]
read_write: PhantomVar<i64>,

#[var(get = get_engine_enum,set = set_engine_enum)]
read_write_engine_enum: PhantomVar<godot::global::VerticalAlignment>,

#[var(get = get_bit_enum,set = set_bit_enum)]
read_write_bit_enum: PhantomVar<godot::global::KeyModifierMask>,

value: i64,

#[init(val = godot::global::VerticalAlignment::CENTER)]
engine_enum_value: godot::global::VerticalAlignment,

#[init(val = godot::global::KeyModifierMask::ALT|godot::global::KeyModifierMask::CTRL)]
bit_enum_value: godot::global::KeyModifierMask,
}

#[godot_api]
Expand All @@ -36,4 +48,24 @@ impl HasPhantomVar {
fn set_read_write(&mut self, value: i64) {
self.value = value;
}

#[func]
fn get_engine_enum(&self) -> godot::global::VerticalAlignment {
self.engine_enum_value
}

#[func]
fn set_engine_enum(&mut self, value: godot::global::VerticalAlignment) {
self.engine_enum_value = value;
}

#[func]
fn get_bit_enum(&self) -> godot::global::KeyModifierMask {
self.bit_enum_value
}

#[func]
fn set_bit_enum(&mut self, value: godot::global::KeyModifierMask) {
self.bit_enum_value = value;
}
}
Loading