Add FromReflect trait to convert dynamic types to concrete types (#1395)

Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).

To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
davier 2021-12-26 18:49:01 +00:00
parent 585d0b8467
commit 06d9384447
14 changed files with 495 additions and 36 deletions

View file

@ -10,14 +10,25 @@ use crate::{
Asset, Assets,
};
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::{Reflect, ReflectDeserialize};
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
use bevy_utils::Uuid;
use crossbeam_channel::{Receiver, Sender};
use serde::{Deserialize, Serialize};
/// A unique, stable asset id
#[derive(
Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect,
Debug,
Clone,
Copy,
Eq,
PartialEq,
Hash,
Ord,
PartialOrd,
Serialize,
Deserialize,
Reflect,
FromReflect,
)]
#[reflect_value(Serialize, Deserialize, PartialEq, Hash)]
pub enum HandleId {
@ -58,7 +69,7 @@ impl HandleId {
///
/// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets)
/// collection.
#[derive(Component, Reflect)]
#[derive(Component, Reflect, FromReflect)]
#[reflect(Component)]
pub struct Handle<T>
where
@ -77,6 +88,13 @@ enum HandleType {
Strong(Sender<RefChange>),
}
// FIXME: This only is needed because `Handle`'s field `handle_type` is currently ignored for reflection
impl Default for HandleType {
fn default() -> Self {
Self::Weak
}
}
impl Debug for HandleType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {

View file

@ -6,7 +6,9 @@ use crate::{
entity::{Entity, EntityMap, MapEntities, MapEntitiesError},
world::{FromWorld, World},
};
use bevy_reflect::{impl_reflect_value, FromType, Reflect, ReflectDeserialize};
use bevy_reflect::{
impl_from_reflect_value, impl_reflect_value, FromType, Reflect, ReflectDeserialize,
};
#[derive(Clone)]
pub struct ReflectComponent {
@ -121,6 +123,7 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
}
impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize));
impl_from_reflect_value!(Entity);
#[derive(Clone)]
pub struct ReflectMapEntities {

View file

@ -0,0 +1,151 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{Field, Generics, Ident, Index, Member, Path};
pub fn impl_struct(
struct_name: &Ident,
generics: &Generics,
bevy_reflect_path: &Path,
active_fields: &[(&Field, usize)],
ignored_fields: &[(&Field, usize)],
) -> TokenStream {
let field_names = active_fields
.iter()
.map(|(field, index)| {
field
.ident
.as_ref()
.map(|i| i.to_string())
.unwrap_or_else(|| index.to_string())
})
.collect::<Vec<String>>();
let field_idents = active_fields
.iter()
.map(|(field, index)| {
field
.ident
.as_ref()
.map(|ident| Member::Named(ident.clone()))
.unwrap_or_else(|| Member::Unnamed(Index::from(*index)))
})
.collect::<Vec<_>>();
let field_types = active_fields
.iter()
.map(|(field, _index)| field.ty.clone())
.collect::<Vec<_>>();
let field_count = active_fields.len();
let ignored_field_idents = ignored_fields
.iter()
.map(|(field, index)| {
field
.ident
.as_ref()
.map(|ident| Member::Named(ident.clone()))
.unwrap_or_else(|| Member::Unnamed(Index::from(*index)))
})
.collect::<Vec<_>>();
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
// Add FromReflect bound for each active field
let mut where_from_reflect_clause = if where_clause.is_some() {
quote! {#where_clause}
} else if field_count > 0 {
quote! {where}
} else {
quote! {}
};
where_from_reflect_clause.extend(quote! {
#(#field_types: #bevy_reflect_path::FromReflect,)*
});
TokenStream::from(quote! {
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
{
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
use #bevy_reflect_path::Struct;
if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() {
Some(
Self{
#(#field_idents: {
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_struct.field(#field_names)?)?
},)*
#(#ignored_field_idents: Default::default(),)*
}
)
} else {
None
}
}
}
})
}
pub fn impl_tuple_struct(
struct_name: &Ident,
generics: &Generics,
bevy_reflect_path: &Path,
active_fields: &[(&Field, usize)],
ignored_fields: &[(&Field, usize)],
) -> TokenStream {
let field_idents = active_fields
.iter()
.map(|(_field, index)| Member::Unnamed(Index::from(*index)))
.collect::<Vec<_>>();
let field_types = active_fields
.iter()
.map(|(field, _index)| field.ty.clone())
.collect::<Vec<_>>();
let field_count = active_fields.len();
let field_indices = (0..field_count).collect::<Vec<usize>>();
let ignored_field_idents = ignored_fields
.iter()
.map(|(_field, index)| Member::Unnamed(Index::from(*index)))
.collect::<Vec<_>>();
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
// Add FromReflect bound for each active field
let mut where_from_reflect_clause = if where_clause.is_some() {
quote! {#where_clause}
} else if field_count > 0 {
quote! {where}
} else {
quote! {}
};
where_from_reflect_clause.extend(quote! {
#(#field_types: #bevy_reflect_path::FromReflect,)*
});
TokenStream::from(quote! {
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
{
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
use #bevy_reflect_path::TupleStruct;
if let #bevy_reflect_path::ReflectRef::TupleStruct(ref_tuple_struct) = reflect.reflect_ref() {
Some(
Self{
#(#field_idents:
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_tuple_struct.field(#field_indices)?)?
,)*
#(#ignored_field_idents: Default::default(),)*
}
)
} else {
None
}
}
}
})
}
pub fn impl_value(type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
TokenStream::from(quote! {
impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause {
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
Some(reflect.any().downcast_ref::<#type_name #ty_generics>()?.clone())
}
}
})
}

View file

@ -1,5 +1,6 @@
extern crate proc_macro;
mod from_reflect;
mod reflect_trait;
mod type_uuid;
@ -740,3 +741,117 @@ pub fn external_type_uuid(tokens: proc_macro::TokenStream) -> proc_macro::TokenS
pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream {
reflect_trait::reflect_trait(args, input)
}
#[proc_macro_derive(FromReflect)]
pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let unit_struct_punctuated = Punctuated::new();
let (fields, mut derive_type) = match &ast.data {
Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) => (&fields.named, DeriveType::Struct),
Data::Struct(DataStruct {
fields: Fields::Unnamed(fields),
..
}) => (&fields.unnamed, DeriveType::TupleStruct),
Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => (&unit_struct_punctuated, DeriveType::UnitStruct),
_ => (&unit_struct_punctuated, DeriveType::Value),
};
let fields_and_args = fields
.iter()
.enumerate()
.map(|(i, f)| {
(
f,
f.attrs
.iter()
.find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME)
.map(|a| {
syn::custom_keyword!(ignore);
let mut attribute_args = PropAttributeArgs { ignore: None };
a.parse_args_with(|input: ParseStream| {
if input.parse::<Option<ignore>>()?.is_some() {
attribute_args.ignore = Some(true);
return Ok(());
}
Ok(())
})
.expect("Invalid 'property' attribute format.");
attribute_args
}),
i,
)
})
.collect::<Vec<(&Field, Option<PropAttributeArgs>, usize)>>();
let active_fields = fields_and_args
.iter()
.filter(|(_field, attrs, _i)| {
attrs.is_none()
|| match attrs.as_ref().unwrap().ignore {
Some(ignore) => !ignore,
None => true,
}
})
.map(|(f, _attr, i)| (*f, *i))
.collect::<Vec<(&Field, usize)>>();
let ignored_fields = fields_and_args
.iter()
.filter(|(_field, attrs, _i)| {
attrs
.as_ref()
.map(|attrs| attrs.ignore.unwrap_or(false))
.unwrap_or(false)
})
.map(|(f, _attr, i)| (*f, *i))
.collect::<Vec<(&Field, usize)>>();
let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");
let type_name = &ast.ident;
for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
let meta_list = if let Meta::List(meta_list) = attribute {
meta_list
} else {
continue;
};
if let Some(ident) = meta_list.path.get_ident() {
if ident == REFLECT_VALUE_ATTRIBUTE_NAME {
derive_type = DeriveType::Value;
}
}
}
match derive_type {
DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct(
type_name,
&ast.generics,
&bevy_reflect_path,
&active_fields,
&ignored_fields,
),
DeriveType::TupleStruct => from_reflect::impl_tuple_struct(
type_name,
&ast.generics,
&bevy_reflect_path,
&active_fields,
&ignored_fields,
),
DeriveType::Value => from_reflect::impl_value(type_name, &ast.generics, &bevy_reflect_path),
}
}
#[proc_macro]
pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream {
let reflect_value_def = parse_macro_input!(input as ReflectDef);
let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");
let ty = &reflect_value_def.type_name;
from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path)
}

View file

@ -1,6 +1,6 @@
use crate as bevy_reflect;
use crate::ReflectDeserialize;
use bevy_reflect_derive::impl_reflect_value;
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
use glam::{IVec2, IVec3, IVec4, Mat3, Mat4, Quat, UVec2, UVec3, UVec4, Vec2, Vec3, Vec4};
impl_reflect_value!(IVec2(PartialEq, Serialize, Deserialize));
@ -15,3 +15,16 @@ impl_reflect_value!(Vec4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Mat3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Mat4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Quat(PartialEq, Serialize, Deserialize));
impl_from_reflect_value!(IVec2);
impl_from_reflect_value!(IVec3);
impl_from_reflect_value!(IVec4);
impl_from_reflect_value!(UVec2);
impl_from_reflect_value!(UVec3);
impl_from_reflect_value!(UVec4);
impl_from_reflect_value!(Vec2);
impl_from_reflect_value!(Vec3);
impl_from_reflect_value!(Vec4);
impl_from_reflect_value!(Mat3);
impl_from_reflect_value!(Mat4);
impl_from_reflect_value!(Quat);

View file

@ -1,11 +1,11 @@
use smallvec::{Array, SmallVec};
use std::any::Any;
use crate::{serde::Serializable, List, ListIter, Reflect, ReflectMut, ReflectRef};
use crate::{serde::Serializable, FromReflect, List, ListIter, Reflect, ReflectMut, ReflectRef};
impl<T: Array + Send + Sync + 'static> List for SmallVec<T>
where
T::Item: Reflect + Clone,
T::Item: FromReflect + Clone,
{
fn get(&self, index: usize) -> Option<&dyn Reflect> {
if index < SmallVec::len(self) {
@ -29,10 +29,12 @@ where
fn push(&mut self, value: Box<dyn Reflect>) {
let value = value.take::<T::Item>().unwrap_or_else(|value| {
panic!(
"Attempted to push invalid value of type {}.",
value.type_name()
)
<T as Array>::Item::from_reflect(&*value).unwrap_or_else(|| {
panic!(
"Attempted to push invalid value of type {}.",
value.type_name()
)
})
});
SmallVec::push(self, value);
}
@ -48,7 +50,7 @@ where
// SAFE: any and any_mut both return self
unsafe impl<T: Array + Send + Sync + 'static> Reflect for SmallVec<T>
where
T::Item: Reflect + Clone,
T::Item: FromReflect + Clone,
{
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
@ -95,3 +97,20 @@ where
None
}
}
impl<T: Array + Send + Sync + 'static> FromReflect for SmallVec<T>
where
T::Item: FromReflect + Clone,
{
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::List(ref_list) = reflect.reflect_ref() {
let mut new_list = Self::with_capacity(ref_list.len());
for field in ref_list.iter() {
new_list.push(<T as Array>::Item::from_reflect(field)?);
}
Some(new_list)
} else {
None
}
}
}

View file

@ -1,11 +1,12 @@
use crate as bevy_reflect;
use crate::{
map_partial_eq, serde::Serializable, DynamicMap, FromType, GetTypeRegistration, List, ListIter,
Map, MapIter, Reflect, ReflectDeserialize, ReflectMut, ReflectRef, TypeRegistration,
map_partial_eq, serde::Serializable, DynamicMap, FromReflect, FromType, GetTypeRegistration,
List, ListIter, Map, MapIter, Reflect, ReflectDeserialize, ReflectMut, ReflectRef,
TypeRegistration,
};
use bevy_reflect_derive::impl_reflect_value;
use bevy_utils::{Duration, HashMap, HashSet};
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
use bevy_utils::{AHashExt, Duration, HashMap, HashSet};
use serde::{Deserialize, Serialize};
use std::{
any::Any,
@ -35,7 +36,34 @@ impl_reflect_value!(HashSet<T: Serialize + Hash + Eq + Clone + for<'de> Deserial
impl_reflect_value!(Range<T: Serialize + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize));
impl_reflect_value!(Duration);
impl<T: Reflect> List for Vec<T> {
impl_from_reflect_value!(bool);
impl_from_reflect_value!(u8);
impl_from_reflect_value!(u16);
impl_from_reflect_value!(u32);
impl_from_reflect_value!(u64);
impl_from_reflect_value!(u128);
impl_from_reflect_value!(usize);
impl_from_reflect_value!(i8);
impl_from_reflect_value!(i16);
impl_from_reflect_value!(i32);
impl_from_reflect_value!(i64);
impl_from_reflect_value!(i128);
impl_from_reflect_value!(isize);
impl_from_reflect_value!(f32);
impl_from_reflect_value!(f64);
impl_from_reflect_value!(String);
impl_from_reflect_value!(
Option<T: Serialize + Clone + for<'de> Deserialize<'de> + Reflect + 'static>
);
impl_from_reflect_value!(
HashSet<T: Serialize + Hash + Eq + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>
);
impl_from_reflect_value!(
Range<T: Serialize + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>
);
impl_from_reflect_value!(Duration);
impl<T: FromReflect> List for Vec<T> {
fn get(&self, index: usize) -> Option<&dyn Reflect> {
<[T]>::get(self, index).map(|value| value as &dyn Reflect)
}
@ -57,17 +85,19 @@ impl<T: Reflect> List for Vec<T> {
fn push(&mut self, value: Box<dyn Reflect>) {
let value = value.take::<T>().unwrap_or_else(|value| {
panic!(
"Attempted to push invalid value of type {}.",
value.type_name()
)
T::from_reflect(&*value).unwrap_or_else(|| {
panic!(
"Attempted to push invalid value of type {}.",
value.type_name()
)
})
});
Vec::push(self, value);
}
}
// SAFE: any and any_mut both return self
unsafe impl<T: Reflect> Reflect for Vec<T> {
unsafe impl<T: FromReflect> Reflect for Vec<T> {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
@ -114,7 +144,7 @@ unsafe impl<T: Reflect> Reflect for Vec<T> {
}
}
impl<T: Reflect + for<'de> Deserialize<'de>> GetTypeRegistration for Vec<T> {
impl<T: FromReflect + for<'de> Deserialize<'de>> GetTypeRegistration for Vec<T> {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Vec<T>>();
registration.insert::<ReflectDeserialize>(FromType::<Vec<T>>::from_type());
@ -122,7 +152,21 @@ impl<T: Reflect + for<'de> Deserialize<'de>> GetTypeRegistration for Vec<T> {
}
}
impl<K: Reflect + Clone + Eq + Hash, V: Reflect + Clone> Map for HashMap<K, V> {
impl<T: FromReflect> FromReflect for Vec<T> {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::List(ref_list) = reflect.reflect_ref() {
let mut new_list = Self::with_capacity(ref_list.len());
for field in ref_list.iter() {
new_list.push(T::from_reflect(field)?);
}
Some(new_list)
} else {
None
}
}
}
impl<K: Reflect + Eq + Hash, V: Reflect> Map for HashMap<K, V> {
fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> {
key.downcast_ref::<K>()
.and_then(|key| HashMap::get(self, key))
@ -163,7 +207,7 @@ impl<K: Reflect + Clone + Eq + Hash, V: Reflect + Clone> Map for HashMap<K, V> {
}
// SAFE: any and any_mut both return self
unsafe impl<K: Reflect + Clone + Eq + Hash, V: Reflect + Clone> Reflect for HashMap<K, V> {
unsafe impl<K: Reflect + Eq + Hash, V: Reflect> Reflect for HashMap<K, V> {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
@ -230,6 +274,22 @@ where
}
}
impl<K: FromReflect + Eq + Hash, V: FromReflect> FromReflect for HashMap<K, V> {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::Map(ref_map) = reflect.reflect_ref() {
let mut new_map = Self::with_capacity(ref_map.len());
for (key, value) in ref_map.iter() {
let new_key = K::from_reflect(key)?;
let new_value = V::from_reflect(value)?;
new_map.insert(new_key, new_value);
}
Some(new_map)
} else {
None
}
}
}
// SAFE: any and any_mut both return self
unsafe impl Reflect for Cow<'static, str> {
fn type_name(&self) -> &str {
@ -298,3 +358,9 @@ impl GetTypeRegistration for Cow<'static, str> {
registration
}
}
impl FromReflect for Cow<'static, str> {
fn from_reflect(reflect: &dyn crate::Reflect) -> Option<Self> {
Some(reflect.any().downcast_ref::<Cow<'static, str>>()?.clone())
}
}

View file

@ -196,7 +196,7 @@ mod tests {
#[test]
fn reflect_complex_patch() {
#[derive(Reflect, Eq, PartialEq, Debug)]
#[derive(Reflect, Eq, PartialEq, Debug, FromReflect)]
#[reflect(PartialEq)]
struct Foo {
a: u32,
@ -206,17 +206,25 @@ mod tests {
d: HashMap<usize, i8>,
e: Bar,
f: (i32, Vec<isize>, Bar),
g: Vec<(Baz, HashMap<usize, Bar>)>,
}
#[derive(Reflect, Eq, PartialEq, Debug)]
#[derive(Reflect, Eq, PartialEq, Clone, Debug, FromReflect)]
#[reflect(PartialEq)]
struct Bar {
x: u32,
}
#[derive(Reflect, Eq, PartialEq, Debug, FromReflect)]
struct Baz(String);
let mut hash_map = HashMap::default();
hash_map.insert(1, 1);
hash_map.insert(2, 2);
let mut hash_map_baz = HashMap::default();
hash_map_baz.insert(1, Bar { x: 0 });
let mut foo = Foo {
a: 1,
_b: 1,
@ -224,6 +232,7 @@ mod tests {
d: hash_map,
e: Bar { x: 1 },
f: (1, vec![1, 2], Bar { x: 1 }),
g: vec![(Baz("string".to_string()), hash_map_baz)],
};
let mut foo_patch = DynamicStruct::default();
@ -250,11 +259,36 @@ mod tests {
tuple.insert(bar_patch);
foo_patch.insert("f", tuple);
let mut composite = DynamicList::default();
composite.push({
let mut tuple = DynamicTuple::default();
tuple.insert({
let mut tuple_struct = DynamicTupleStruct::default();
tuple_struct.insert("new_string".to_string());
tuple_struct
});
tuple.insert({
let mut map = DynamicMap::default();
map.insert(1usize, {
let mut struct_ = DynamicStruct::default();
struct_.insert("x", 7u32);
struct_
});
map
});
tuple
});
foo_patch.insert("g", composite);
foo.apply(&foo_patch);
let mut hash_map = HashMap::default();
hash_map.insert(1, 1);
hash_map.insert(2, 3);
let mut hash_map_baz = HashMap::default();
hash_map_baz.insert(1, Bar { x: 7 });
let expected_foo = Foo {
a: 2,
_b: 1,
@ -262,9 +296,28 @@ mod tests {
d: hash_map,
e: Bar { x: 2 },
f: (2, vec![3, 4, 5], Bar { x: 2 }),
g: vec![(Baz("new_string".to_string()), hash_map_baz.clone())],
};
assert_eq!(foo, expected_foo);
let new_foo = Foo::from_reflect(&foo_patch)
.expect("error while creating a concrete type from a dynamic type");
let mut hash_map = HashMap::default();
hash_map.insert(2, 3);
let expected_new_foo = Foo {
a: 2,
_b: 0,
c: vec![3, 4, 5],
d: hash_map,
e: Bar { x: 2 },
f: (2, vec![3, 4, 5], Bar { x: 2 }),
g: vec![(Baz("new_string".to_string()), hash_map_baz)],
};
assert_eq!(new_foo, expected_new_foo);
}
#[test]

View file

@ -326,7 +326,7 @@ mod tests {
bar: C,
}
#[derive(Reflect)]
#[derive(Reflect, FromReflect)]
struct C {
baz: f32,
}

View file

@ -47,6 +47,11 @@ pub unsafe trait Reflect: Any + Send + Sync {
fn serializable(&self) -> Option<Serializable>;
}
pub trait FromReflect: Reflect + Sized {
/// Creates a clone of a reflected value, converting it to a concrete type if it was a dynamic types (e.g. [`DynamicStruct`](crate::DynamicStruct))
fn from_reflect(reflect: &dyn Reflect) -> Option<Self>;
}
impl Debug for dyn Reflect {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Reflect({})", self.type_name())

View file

@ -1,6 +1,6 @@
use std::any::Any;
use crate::{serde::Serializable, Reflect, ReflectMut, ReflectRef};
use crate::{serde::Serializable, FromReflect, Reflect, ReflectMut, ReflectRef};
pub trait Tuple: Reflect {
fn field(&self, index: usize) -> Option<&dyn Reflect>;
@ -329,6 +329,23 @@ macro_rules! impl_reflect_tuple {
None
}
}
impl<$($name: FromReflect),*> FromReflect for ($($name,)*)
{
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::Tuple(_ref_tuple) = reflect.reflect_ref() {
Some(
(
$(
<$name as FromReflect>::from_reflect(_ref_tuple.field($index)?)?,
)*
)
)
} else {
None
}
}
}
}
}

View file

@ -5,11 +5,11 @@ pub use colorspace::*;
use crate::color::{HslRepresentation, SrgbColorSpace};
use bevy_core::Bytes;
use bevy_math::{Vec3, Vec4};
use bevy_reflect::{Reflect, ReflectDeserialize};
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
use serde::{Deserialize, Serialize};
use std::ops::{Add, AddAssign, Mul, MulAssign};
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect, FromReflect)]
#[reflect(PartialEq, Serialize, Deserialize)]
pub enum Color {
/// sRGBA color

View file

@ -41,8 +41,7 @@ impl Plugin for TextPlugin {
fn build(&self, app: &mut App) {
app.add_asset::<Font>()
.add_asset::<FontAtlasSet>()
// TODO: uncomment when #2215 is fixed
// .register_type::<Text>()
.register_type::<Text>()
.register_type::<VerticalAlign>()
.register_type::<HorizontalAlign>()
.init_asset_loader::<FontLoader>()

View file

@ -1,7 +1,7 @@
use bevy_asset::Handle;
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
use bevy_math::Size;
use bevy_reflect::{Reflect, ReflectDeserialize};
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
use bevy_render::color::Color;
use serde::{Deserialize, Serialize};
@ -65,7 +65,7 @@ impl Text {
}
}
#[derive(Debug, Default, Clone, Reflect)]
#[derive(Debug, Default, Clone, FromReflect, Reflect)]
pub struct TextSection {
pub value: String,
pub style: TextStyle,
@ -134,7 +134,7 @@ impl From<VerticalAlign> for glyph_brush_layout::VerticalAlign {
}
}
#[derive(Clone, Debug, Reflect)]
#[derive(Clone, Debug, Reflect, FromReflect)]
pub struct TextStyle {
pub font: Handle<Font>,
pub font_size: f32,