mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
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:
parent
585d0b8467
commit
06d9384447
14 changed files with 495 additions and 36 deletions
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
151
crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs
Normal file
151
crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -326,7 +326,7 @@ mod tests {
|
|||
bar: C,
|
||||
}
|
||||
|
||||
#[derive(Reflect)]
|
||||
#[derive(Reflect, FromReflect)]
|
||||
struct C {
|
||||
baz: f32,
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue