diff --git a/crates/bevy_feathers/src/controls/button.rs b/crates/bevy_feathers/src/controls/button.rs index 1b093def9bb57..38b1d8e0588d9 100644 --- a/crates/bevy_feathers/src/controls/button.rs +++ b/crates/bevy_feathers/src/controls/button.rs @@ -126,7 +126,7 @@ pub struct FeathersToolButton; impl FeathersToolButton { fn scene(props: FeathersButtonProps) -> impl Scene { bsn! { - :FeathersButton { + @FeathersButton { @caption: {props.caption}, @variant: {props.variant}, @corners: {props.corners} diff --git a/crates/bevy_feathers/src/controls/disclosure_toggle.rs b/crates/bevy_feathers/src/controls/disclosure_toggle.rs index 16634e1c35894..6f6165b722d57 100644 --- a/crates/bevy_feathers/src/controls/disclosure_toggle.rs +++ b/crates/bevy_feathers/src/controls/disclosure_toggle.rs @@ -45,7 +45,7 @@ impl FeathersDisclosureToggle { FocusIndicator TabIndex(0) Children [ - :icon(icons::CHEVRON_RIGHT) + icon(icons::CHEVRON_RIGHT) ] ) } diff --git a/crates/bevy_feathers/src/controls/menu.rs b/crates/bevy_feathers/src/controls/menu.rs index 70101596ae48c..2a3dc95b0bb91 100644 --- a/crates/bevy_feathers/src/controls/menu.rs +++ b/crates/bevy_feathers/src/controls/menu.rs @@ -158,7 +158,7 @@ impl Default for FeathersMenuButtonProps { impl FeathersMenuButton { fn scene(props: FeathersMenuButtonProps) -> impl Scene { bsn! { - :FeathersButton { + @FeathersButton { @caption: {props.caption}, @variant: ButtonVariant::Normal, @corners: {props.corners}, @@ -174,7 +174,7 @@ impl FeathersMenuButton { Node { flex_grow: 1.0, }, - :icon(icons::CHEVRON_DOWN), + icon(icons::CHEVRON_DOWN), )) as Box } else { Box::new(bsn_list!()) as Box diff --git a/crates/bevy_feathers/src/controls/number_input.rs b/crates/bevy_feathers/src/controls/number_input.rs index 4c1298984fb2e..c686c0fedeff5 100644 --- a/crates/bevy_feathers/src/controls/number_input.rs +++ b/crates/bevy_feathers/src/controls/number_input.rs @@ -77,7 +77,7 @@ impl Default for FeathersNumberInputProps { impl FeathersNumberInput { fn scene(props: FeathersNumberInputProps) -> impl Scene { bsn! { - :FeathersTextInputContainer + :@FeathersTextInputContainer ThemeBorderColor({props.sigil_color}) FeathersNumberInput template_value(props.number_format) @@ -108,7 +108,7 @@ impl FeathersNumberInput { None => Box::new(bsn_list!()) as Box } } - :FeathersTextInput { + @FeathersTextInput { @max_characters: 20usize, } SelectAllOnFocus diff --git a/crates/bevy_feathers/src/controls/virtual_keyboard.rs b/crates/bevy_feathers/src/controls/virtual_keyboard.rs index 6e63d78364559..4c679cc87bc15 100644 --- a/crates/bevy_feathers/src/controls/virtual_keyboard.rs +++ b/crates/bevy_feathers/src/controls/virtual_keyboard.rs @@ -40,7 +40,7 @@ impl + Clone + Send + Sync + 'static> VirtualKeyboard { let key_row = Vec::from_iter(row.into_iter().map(move |key| { let key_clone = key.clone(); bsn! { - :FeathersButton + :@FeathersButton Node { flex_grow: 1.0, } diff --git a/crates/bevy_scene/macros/src/bsn/codegen.rs b/crates/bevy_scene/macros/src/bsn/codegen.rs index 66861e0a90416..df9ab3fbb614b 100644 --- a/crates/bevy_scene/macros/src/bsn/codegen.rs +++ b/crates/bevy_scene/macros/src/bsn/codegen.rs @@ -1,7 +1,7 @@ use crate::bsn::types::{ - Bsn, BsnConstructor, BsnEntry, BsnFields, BsnInheritedScene, BsnListRoot, BsnRelatedSceneList, - BsnRoot, BsnSceneFn, BsnSceneFnArg, BsnSceneFnArgs, BsnSceneListItem, BsnSceneListItems, - BsnType, BsnValue, + Bsn, BsnConstructor, BsnEntry, BsnFields, BsnListRoot, BsnRelatedSceneList, BsnRoot, BsnScene, + BsnSceneFn, BsnSceneFnArg, BsnSceneFnArgs, BsnSceneListItem, BsnSceneListItems, BsnType, + BsnValue, }; use bevy_macro_utils::{fq_std::FQDefault, path_to_string}; use proc_macro2::TokenStream; @@ -228,7 +228,6 @@ impl BsnEntry { let value = _scene.get_or_insert_template::<#type_path>(_context); *value = #type_path::#const_ident; }), - BsnEntry::SceneExpression(block) => EntryResult::NewSceneImpl(quote! {{#block}}), BsnEntry::TemplateConstructor(BsnConstructor { type_path, function, @@ -258,54 +257,11 @@ impl BsnEntry { let scenes = scene_list.0.to_tokens(ctx); EntryResult::NewSceneImpl(quote! { #bevy_scene::RelatedScenes::<<#relationship_path as #bevy_ecs::relationship::RelationshipTarget> - ::Relationship, _>::new(#scenes) + ::Relationship, _>::new(#scenes) }) } - BsnEntry::SceneFn(func) => EntryResult::NewSceneImpl(func.to_tokens(ctx)), - BsnEntry::InheritedScene(s) => EntryResult::NewSceneImpl(match s { - BsnInheritedScene::Asset(lit) => quote! { - #bevy_scene::InheritSceneAsset::from(#lit) - }, - BsnInheritedScene::Fn(func) => func.to_tokens(ctx), - BsnInheritedScene::Type(bsn_type) => { - // TODO: this can and should use a simpler codegen path than BsnType::to_patch_tokens, - // which imposes constraints like requiring the type to impl FromTemplate, and requiring - // enums to have VariantDefault. - let mut assignments = Vec::new(); - let props = format_ident!("props"); - let props_ref = format_ident!("props_ref"); - let target = PatchTarget { - path: &[Member::Named(props_ref.clone())], - is_ref: true, - }; - bsn_type.to_patch_tokens(ctx, &mut assignments, false, true, true, target)?; - let mut assigns = Vec::new(); - let target = PatchTarget { - path: &[Member::Named(Ident::new( - "value", - proc_macro2::Span::call_site(), - ))], - is_ref: true, - }; - bsn_type.to_patch_tokens(ctx, &mut assigns, true, false, true, target)?; - let path = &bsn_type.path; - let bevy_scene = ctx.bevy_scene; - let from_template_patch = quote! { - <#path as #bevy_scene::PatchFromTemplate>::patch(move |value, _context| { - #(#assigns)* - }) - }; - quote! {{ - let mut #props = <<#path as #bevy_scene::SceneComponent>::Props as #FQDefault>::default(); - let #props_ref = &mut #props; - #(#assignments)* - (<#path as #bevy_scene::SceneComponent>::scene(#props), #from_template_patch) - }} - } - BsnInheritedScene::Expression(tokens) => quote! { - #tokens - }, - }), + BsnEntry::UncachedScene(s) => EntryResult::NewSceneImpl(s.to_tokens(ctx)?), + BsnEntry::CachedScene(s) => EntryResult::NewSceneImpl(s.to_tokens(ctx)?), BsnEntry::Name(ident) => { let (name, index) = (ident.to_string(), ctx.entity_refs.get(ident.to_string())); let invocation = ctx.invocation_index.clone(); @@ -321,6 +277,56 @@ impl BsnEntry { } } +impl BsnScene { + fn to_tokens(&self, ctx: &mut BsnCodegenCtx) -> syn::Result { + let bevy_scene = ctx.bevy_scene; + match self { + BsnScene::Asset(lit) => Ok(quote! { + #bevy_scene::CachedSceneAsset::from(#lit) + }), + BsnScene::Fn(func) => Ok(func.to_tokens(ctx)), + BsnScene::SceneComponent(bsn_type) => { + // TODO: this can and should use a simpler codegen path than BsnType::to_patch_tokens, + // which imposes constraints like requiring the type to impl FromTemplate, and requiring + // enums to have VariantDefault. + let mut assignments = Vec::new(); + let props = format_ident!("props"); + let props_ref = format_ident!("props_ref"); + let target = PatchTarget { + path: &[Member::Named(props_ref.clone())], + is_ref: true, + }; + bsn_type.to_patch_tokens(ctx, &mut assignments, false, true, true, target)?; + let mut assigns = Vec::new(); + let target = PatchTarget { + path: &[Member::Named(Ident::new( + "value", + proc_macro2::Span::call_site(), + ))], + is_ref: true, + }; + bsn_type.to_patch_tokens(ctx, &mut assigns, true, false, true, target)?; + let path = &bsn_type.path; + let bevy_scene = ctx.bevy_scene; + let from_template_patch = quote! { + <#path as #bevy_scene::PatchFromTemplate>::patch(move |value, _context| { + #(#assigns)* + }) + }; + Ok(quote! {{ + let mut #props = <<#path as #bevy_scene::SceneComponent>::Props as #FQDefault>::default(); + let #props_ref = &mut #props; + #(#assignments)* + (<#path as #bevy_scene::SceneComponent>::scene(#props), #from_template_patch) + }}) + } + BsnScene::Expression(tokens) => Ok(quote! { + #tokens + }), + } + } +} + impl BsnType { /// Recursively generates token streams. fn to_patch_tokens( @@ -447,8 +453,8 @@ impl BsnType { format!( "Scene prop fields are not supported in normal component patches\ . If you would like to set a component scene's prop field, it \ - should be set using \"scene inheritance\": \ - bsn! {{ :{} {{ @{field_name}: VALUE }} }}", + should be set using \"scene component\" syntax: \ + bsn! {{ @{} {{ @{field_name}: VALUE }} }}", path_to_string(type_path) ), )); diff --git a/crates/bevy_scene/macros/src/bsn/parse.rs b/crates/bevy_scene/macros/src/bsn/parse.rs index 7ec39bfa82eb8..c63699e0b42a2 100644 --- a/crates/bevy_scene/macros/src/bsn/parse.rs +++ b/crates/bevy_scene/macros/src/bsn/parse.rs @@ -1,7 +1,7 @@ use crate::bsn::types::{ - Bsn, BsnConstructor, BsnEntry, BsnFields, BsnInheritedScene, BsnListRoot, BsnNamedField, - BsnRelatedSceneList, BsnRoot, BsnSceneFn, BsnSceneFnArg, BsnSceneFnArgs, BsnSceneList, - BsnSceneListItem, BsnSceneListItems, BsnTuple, BsnType, BsnUnnamedField, BsnValue, + Bsn, BsnConstructor, BsnEntry, BsnFields, BsnListRoot, BsnNamedField, BsnRelatedSceneList, + BsnRoot, BsnScene, BsnSceneFn, BsnSceneFnArg, BsnSceneFnArgs, BsnSceneList, BsnSceneListItem, + BsnSceneListItems, BsnTuple, BsnType, BsnUnnamedField, BsnValue, }; use bevy_macro_utils::{path_to_string, PathType}; use proc_macro2::{Delimiter, TokenStream, TokenTree}; @@ -12,7 +12,7 @@ use syn::{ parenthesized, parse::{Parse, ParseBuffer, ParseStream}, spanned::Spanned, - token::{At, Brace, Bracket, Colon, Comma, Paren}, + token::{At, Brace, Bracket, Colon, Comma, Paren, Tilde}, Block, Expr, Ident, Lit, LitStr, Path, Result, Token, }; @@ -59,22 +59,22 @@ impl Parse for BsnListRoot { impl Parse for Bsn { fn parse(input: ParseStream) -> Result { let mut entries = Vec::new(); - let mut found_inherited_scene = false; + let mut found_cached_scene = false; if input.peek(Paren) { let content; parenthesized![content in input]; while !content.is_empty() { - let entry = BsnEntry::parse(&content, found_inherited_scene)?; - if matches!(entry, BsnEntry::InheritedScene(_)) { - found_inherited_scene = true; + let entry = BsnEntry::parse(&content, found_cached_scene)?; + if matches!(entry, BsnEntry::CachedScene(_)) { + found_cached_scene = true; } entries.push(entry); } } else if ALLOW_FLAT { while !input.is_empty() { - let entry = BsnEntry::parse(input, found_inherited_scene)?; - if matches!(entry, BsnEntry::InheritedScene(_)) { - found_inherited_scene = true; + let entry = BsnEntry::parse(input, found_cached_scene)?; + if matches!(entry, BsnEntry::CachedScene(_)) { + found_cached_scene = true; } entries.push(entry); if input.peek(Comma) { @@ -84,7 +84,7 @@ impl Parse for Bsn { } } } else { - entries.push(BsnEntry::parse(input, found_inherited_scene)?); + entries.push(BsnEntry::parse(input, found_cached_scene)?); } Ok(Self { entries }) @@ -92,9 +92,9 @@ impl Parse for Bsn { } impl BsnEntry { - fn parse(input: ParseStream, found_inherited_scene: bool) -> Result { + fn parse(input: ParseStream, found_cached_scene: bool) -> Result { Ok(if input.peek(Token![:]) { - BsnEntry::InheritedScene(BsnInheritedScene::parse(input, found_inherited_scene)?) + BsnEntry::CachedScene(BsnScene::parse(input, found_cached_scene)?) } else if input.peek(Token![#]) { input.parse::()?; if input.peek(Brace) { @@ -102,12 +102,12 @@ impl BsnEntry { } else { BsnEntry::Name(input.parse::()?) } - } else if input.peek(Brace) { - BsnEntry::SceneExpression(braced_tokens(input)?) + } else if input.peek(Brace) || input.peek(At) { + BsnEntry::UncachedScene(BsnScene::parse(input, found_cached_scene)?) } else { - let is_template = input.peek(At); + let is_template = input.peek(Tilde); if is_template { - input.parse::()?; + input.parse::()?; } let mut path = input.parse::()?; let path_type = PathType::new(&path); @@ -168,9 +168,9 @@ impl BsnEntry { PathType::Function => { if input.peek(Paren) { let args = input.parse()?; - BsnEntry::SceneFn(BsnSceneFn { path, args }) + BsnEntry::UncachedScene(BsnScene::Fn(BsnSceneFn { path, args })) } else { - BsnEntry::SceneExpression(quote! {#path}) + BsnEntry::UncachedScene(BsnScene::Expression(quote! {#path})) } } } @@ -231,39 +231,76 @@ impl Parse for BsnSceneFnArg { } } } -impl BsnInheritedScene { - fn parse(input: ParseStream, found_inherited_scene: bool) -> Result { - let colon = input.parse::()?; - if found_inherited_scene { - return Err(syn::Error::new( - colon.span(), - "Cannot inherit scenes more than once", - )); +impl BsnScene { + fn parse(input: ParseStream, found_cached_scene: bool) -> Result { + let cached = if input.peek(Token![:]) { + Some(input.parse::()?) + } else { + None + }; + + let err_if_cached = |msg: &str| { + if let Some(colon) = cached { + Err(syn::Error::new(colon.span(), msg)) + } else { + Ok(()) + } + }; + + if found_cached_scene { + err_if_cached("Cannot cache scenes more than once")?; } + Ok(if input.peek(LitStr) { let path = input.parse::()?; - BsnInheritedScene::Asset(path) + if cached.is_none() { + return Err(syn::Error::new( + path.span(), + "Cannot use scenes from asset path without caching, please add the ':' prefix.", + )); + } + BsnScene::Asset(path) } else if input.peek(Brace) { - BsnInheritedScene::Expression(braced_tokens(input)?) + err_if_cached("Cannot cache scene expressions")?; + BsnScene::Expression(braced_tokens(input)?) + } else if input.peek(At) { + input.parse::()?; + let sc = input.parse::()?; + if sc.fields.len() > 0 { + err_if_cached("Cannot cache Scene Components with props/fields")?; + } + BsnScene::SceneComponent(sc) } else { // PERF: do we really need this fork here? let path = input.fork().parse::()?; match PathType::new(&path) { PathType::Type | PathType::Enum => { - BsnInheritedScene::Type(input.parse::()?) + // Scene components are parsed before this if an @ is found. + // If this path is hit, that means it wasn't prefixed by @ + return Err(syn::Error::new( + path.span(), + format!( + "Scene component {} needs to be prefixed by '@'", + path_to_string(&path), + ), + )); } PathType::Function | PathType::TypeFunction => { let path = input.parse::()?; - BsnInheritedScene::Fn(BsnSceneFn { + let func = BsnSceneFn { path, args: input.parse()?, - }) + }; + if func.args.0.is_some() { + err_if_cached("Cannot cache Scene function with arguments")?; + } + BsnScene::Fn(func) } path_type => { return Err(syn::Error::new( path.span(), format!( - "Cannot inherit from path {} of type {:?}", + "Cannot cache path {} of type {:?}", path_to_string(&path), path_type, ), diff --git a/crates/bevy_scene/macros/src/bsn/types.rs b/crates/bevy_scene/macros/src/bsn/types.rs index 60c6e3a76d013..bfbf9bf043c43 100644 --- a/crates/bevy_scene/macros/src/bsn/types.rs +++ b/crates/bevy_scene/macros/src/bsn/types.rs @@ -21,9 +21,8 @@ pub enum BsnEntry { FromTemplateConstructor(BsnConstructor), TemplateConstructor(BsnConstructor), TemplateConst { type_path: Path, const_ident: Ident }, - SceneExpression(TokenStream), - InheritedScene(BsnInheritedScene), - SceneFn(BsnSceneFn), + UncachedScene(BsnScene), + CachedScene(BsnScene), RelatedSceneList(BsnRelatedSceneList), } @@ -68,10 +67,10 @@ pub struct BsnSceneFn { } #[derive(Debug)] -pub enum BsnInheritedScene { +pub enum BsnScene { Asset(LitStr), Fn(BsnSceneFn), - Type(BsnType), + SceneComponent(BsnType), Expression(TokenStream), } @@ -87,6 +86,14 @@ pub enum BsnFields { Named(Vec), Tuple(Vec), } +impl BsnFields { + pub fn len(&self) -> usize { + match self { + BsnFields::Named(vec) => vec.len(), + BsnFields::Tuple(vec) => vec.len(), + } + } +} #[derive(Debug)] pub struct BsnTuple(pub Vec); diff --git a/crates/bevy_scene/macros/src/lib.rs b/crates/bevy_scene/macros/src/lib.rs index 3c63429c45ccf..66e5bacd79eac 100644 --- a/crates/bevy_scene/macros/src/lib.rs +++ b/crates/bevy_scene/macros/src/lib.rs @@ -126,7 +126,7 @@ use syn::{parse_macro_input, DeriveInput}; /// References can point in any direction within the scene: a parent can reference a descendant, /// a child can reference a parent, and siblings within the same `Children [...]` block can /// reference each other. -/// All names in a single `bsn!` call share one scope; names from composed or inherited scenes +/// All names in a single `bsn!` call share one scope; names from composed or cached scenes /// (`my_scene()`, `:my_scene`) live in their own separate scopes and are not visible here. /// If two scopes both define the same name (e.g. both use `#Player`), each `#Player` resolves /// to its own entity — there is no conflict or shadowing. @@ -326,7 +326,7 @@ use syn::{parse_macro_input, DeriveInput}; /// **Inheritance** uses the `:` prefix. The parent is *pre-resolved* first — its templates are /// fully flattened into a `ResolvedScene` — and the child's patches are applied on top. /// When the scene is parameterless, this will "cache" the scene and share it across all inheriting scenes. -/// For larger scenes that are inherited many times, this can be much faster than re-computing +/// For larger scenes that are cached and spawned many times, this can be much faster than re-computing /// the scene each time. /// /// ```rust, ignore diff --git a/crates/bevy_scene/macros/src/scene_component.rs b/crates/bevy_scene/macros/src/scene_component.rs index a278cd76d0f25..3f5c4609ec880 100644 --- a/crates/bevy_scene/macros/src/scene_component.rs +++ b/crates/bevy_scene/macros/src/scene_component.rs @@ -35,7 +35,7 @@ pub(crate) fn derive_scene_component(ast: &mut DeriveInput) -> TokenStream { let (scene_impl, props_type) = match scene { Scene::Function(path) => (quote! {#path()}, quote! {()}), Scene::Asset(lit_str) => ( - quote! {#bevy_scene::InheritSceneAsset::from(#lit_str)}, + quote! {#bevy_scene::CachedSceneAsset::from(#lit_str)}, quote! {()}, ), Scene::FunctionProps { function, props } => (quote! {#function(props)}, quote! {#props}), diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs index b46fe3889b914..5367879a27571 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -184,7 +184,7 @@ //! //! Each [`bsn!`] invocation creates its own name scope. A name is visible to the root //! entity, its children, and any deeper descendants — as long as the reference is written -//! in the same [`bsn!`] call. Composed or inherited scenes (via `my_scene()` or `:my_scene`) +//! in the same [`bsn!`] call. Composed or cached scenes (via `my_scene()` or `:my_scene`) //! each bring their own separate scope, so names do not leak across scene boundaries. //! //! If both a parent and a composed child define the same name (e.g. both use `#X`), @@ -527,7 +527,7 @@ //! # } //! # let mut world = World::new(); //! world.spawn_scene(bsn! { -//! :Player { score: 0 } +//! @Player { score: 0 } //! }); //! ``` //! @@ -545,7 +545,7 @@ //! - Scene inheritance syntax: constructs the full scene and inherits from it //! - Component patch syntax: _Just_ patches the component directly and creates it if it doesn't exist. //! This will not do any scene inheritance. You can still patch scene components this way as long -//! as the scene component is inherited somewhere "earlier" in the inheritance hierarchy. +//! as the scene component is cached somewhere "earlier" in the inheritance hierarchy. //! //! ### Custom Scene Functions //! @@ -617,7 +617,7 @@ //! } //! //! world.spawn_scene(bsn! { -//! :Player { image: "player.png" } +//! @Player { image: "player.png" } //! }); //! ``` //! @@ -659,7 +659,7 @@ //! } //! //! world.spawn_scene(bsn! { -//! :HelloRepeater { +//! @HelloRepeater { //! @repeat: 5 //! } //! }); @@ -689,7 +689,7 @@ //! } //! //! world.spawn_scene(bsn! { -//! :Widget { +//! @Widget { //! @border: true, //! value: 10, //! } @@ -876,7 +876,7 @@ mod tests { } #[test] - fn inheritance_patching() { + fn cached_patching() { let mut app = test_app(); let world = app.world_mut(); @@ -923,7 +923,7 @@ mod tests { } #[test] - fn loaded_asset_inheritance_patching() { + fn loaded_asset_cached_patching() { #[derive(Component, FromTemplate)] struct Position { x: f32, @@ -1027,7 +1027,7 @@ mod tests { // "a.bsn" as AWidget's "component scene" let id = world - .spawn_scene(bsn! {:AWidget { value: 2 }}) + .spawn_scene(bsn! {@AWidget { value: 2 }}) .unwrap() .id(); let root = world.entity(id); @@ -1783,14 +1783,14 @@ mod tests { let mut app = test_app(); let world = app.world_mut(); - let entity = world.spawn_scene(bsn! {:Widget}).unwrap(); + let entity = world.spawn_scene(bsn! {@Widget}).unwrap(); assert_eq!(entity.get::().unwrap().as_str(), "widget"); assert!(entity.contains::()); #[derive(SceneComponent, Default, Clone)] #[scene(Widget::scene)] struct OtherWidget; - let entity = world.spawn_scene(bsn! {:OtherWidget}).unwrap(); + let entity = world.spawn_scene(bsn! {@OtherWidget}).unwrap(); assert_eq!(entity.get::().unwrap().as_str(), "widget"); assert!(entity.contains::()); assert!( @@ -1832,7 +1832,7 @@ mod tests { let mut app = test_app(); let world = app.world_mut(); let entity = world - .spawn_scene(bsn! {:Widget { + .spawn_scene(bsn! {@Widget { @children: 2, value: 10, }}) @@ -1847,7 +1847,7 @@ mod tests { } let entity = world - .spawn_scene(bsn! {:OtherWidget { + .spawn_scene(bsn! {@OtherWidget { @children: 2, value: 10, }}) @@ -1869,7 +1869,7 @@ mod tests { let mut app = test_app(); let world = app.world_mut(); - let entity = world.spawn_scene(bsn! {:Widget}).unwrap(); + let entity = world.spawn_scene(bsn! {@Widget}).unwrap(); assert!(entity.contains::()); } @@ -1887,7 +1887,7 @@ mod tests { let scene = bsn! { #Name Children [ - :Widget(#Name) + @Widget(#Name) ] }; @@ -1916,7 +1916,7 @@ mod tests { let scene = bsn! { #Name Children [ - :Widget{ + @Widget{ entity: #Name } ] @@ -1944,46 +1944,25 @@ mod tests { let mut app = test_app(); let world = app.world_mut(); - let inherit_pass_expr = bsn! { - #Name - Children [ - :widget(#{Entity::PLACEHOLDER}) - ] - }; - let entity = world.spawn_scene(inherit_pass_expr).unwrap().id(); - let root = world.entity(entity); - let children = root.get::().unwrap(); - let child_widget = world.entity(children[0]).get::().unwrap(); - assert_eq!(child_widget.0, Entity::PLACEHOLDER); - let noninherit_pass_expr = bsn! { + let pass_expr = bsn! { #Name Children [ widget(#{Entity::PLACEHOLDER}) ] }; - let entity = world.spawn_scene(noninherit_pass_expr).unwrap().id(); + let entity = world.spawn_scene(pass_expr).unwrap().id(); let root = world.entity(entity); let children = root.get::().unwrap(); let child_widget = world.entity(children[0]).get::().unwrap(); assert_eq!(child_widget.0, Entity::PLACEHOLDER); - let inherit_pass_name = bsn! { - #Name - Children [ - :widget(#Name) - ] - }; - let entity = world.spawn_scene(inherit_pass_name).unwrap().id(); - let root = world.entity(entity); - let children = root.get::().unwrap(); - let child_widget = world.entity(children[0]).get::().unwrap(); - assert_eq!(child_widget.0, entity); - let noninherit_pass_name = bsn! { + + let pass_name = bsn! { #Name Children [ widget(#Name) ] }; - let entity = world.spawn_scene(noninherit_pass_name).unwrap().id(); + let entity = world.spawn_scene(pass_name).unwrap().id(); let root = world.entity(entity); let children = root.get::().unwrap(); let child_widget = world.entity(children[0]).get::().unwrap(); @@ -2017,7 +1996,7 @@ mod tests { let prop_expr = bsn! { Children [ - :Widget { + @Widget { @entity: Entity::PLACEHOLDER } ] @@ -2030,7 +2009,7 @@ mod tests { let scene_prop = bsn! { #Name Children [ - :Widget { + @Widget { @entity: #Name } ] @@ -2093,7 +2072,7 @@ mod tests { } #[test] - fn inheritance_with_generics() { + fn caching_with_generics() { #[derive(Component, FromTemplate, PartialEq, Eq, Debug)] struct Foo>> { value: T, diff --git a/crates/bevy_scene/src/resolved_scene.rs b/crates/bevy_scene/src/resolved_scene.rs index 48774b8851ed1..6d451fb5ef703 100644 --- a/crates/bevy_scene/src/resolved_scene.rs +++ b/crates/bevy_scene/src/resolved_scene.rs @@ -33,7 +33,7 @@ impl ResolvedSceneRoot { &mut ResolveContext { assets, patches, - inherited: None, + cached: None, }, &mut resolved_scene, )?; @@ -61,7 +61,7 @@ impl ResolvedSceneRoot { /// This will apply all of the [`Template`]s in this root [`ResolvedScene`] to the entity. It will also /// spawn all of this [`ResolvedScene`]'s related entities. /// - /// If this root [`ResolvedScene`] inherits from another scene, that scene will be applied _first_. + /// If this root [`ResolvedScene`] includes a cached scene, that scene will be applied _first_. pub fn apply( &self, entity: &mut EntityWorldMut, @@ -100,7 +100,7 @@ impl ResolvedSceneListRoot { &mut ResolveContext { assets, patches, - inherited: None, + cached: None, }, &mut resolved_scenes, )?; @@ -151,12 +151,12 @@ impl ResolvedSceneListRoot { /// A final resolved scene (usually produced by calling [`Scene::resolve`]). This consists of: /// 1. A collection of [`Template`]s to apply to a spawned [`Entity`], which are stored as [`ErasedComponentTemplate`]s and [`ErasedBundleTemplate`]s. /// 2. A collection of [`RelatedResolvedScenes`], which will be spawned as "related" entities (ex: [`Children`] entities). -/// 3. The inherited [`ScenePatch`] if it exists. +/// 3. An optional cached [`ScenePatch`]. /// -/// This uses "copy-on-write" behavior for inherited scenes. If a [`Template`] that the inherited scene has is requested, it will be -/// cloned (using [`Template::clone_template`]) and added to the current [`ResolvedScene`]. +/// This uses "copy-on-write" behavior for cached scenes. If a [`Template`] is requested which the cached scene has as well, +/// it will be cloned (using [`Template::clone_template`]) and added to the current [`ResolvedScene`]. /// -/// When applying this [`ResolvedScene`] to an [`Entity`], the inherited scene (including its children) is applied _first_. _Then_ this +/// When applying this [`ResolvedScene`] to an [`Entity`], the cached scene (including its related scenes) is applied _first_. _Then_ this /// [`ResolvedScene`] is applied. /// /// [`Scene::resolve`]: crate::Scene::resolve @@ -172,20 +172,20 @@ pub struct ResolvedScene { /// [`Children`]: bevy_ecs::hierarchy::Children // PERF: special casing Children might make sense here to avoid hashing related: TypeIdMap, - /// The inherited [`ScenePatch`] to apply _first_ before applying this [`ResolvedScene`]. - inherited: Option, + /// The cached [`ScenePatch`] to apply _first_ before applying this [`ResolvedScene`]. + cached: Option, /// A [`TypeId`] to `templates` index mapping. If a [`Template`] is intended to be shared / patched across scenes, it should be registered /// here. template_indices: TypeIdMap, /// A list of all [`SceneEntityReference`] values associated with this entity. There can be more than one if this scene uses - /// "flattened" inheritance. + /// "flattened" caching. pub entity_references: Vec, } impl core::fmt::Debug for ResolvedScene { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("ResolvedScene") - .field("inherited", &self.inherited) + .field("cached", &self.cached) .field("template_types", &self.template_indices.keys()) .field("related", &self.related) .field("entity_references", &self.entity_references) @@ -199,7 +199,7 @@ impl ResolvedScene { /// This will apply all of the [`Template`]s in this [`ResolvedScene`] to the entity in the [`TemplateContext`]. It will also /// spawn all of this [`ResolvedScene`]'s related entities. /// - /// If this [`ResolvedScene`] inherits from another scene, that scene will be applied _first_. + /// If this [`ResolvedScene`] includes a cached scene, that scene will be applied _first_. fn apply( &self, context: &mut TemplateContext, @@ -213,7 +213,7 @@ impl ResolvedScene { /// This will apply all of the [`Template`]s in this [`ResolvedScene`] to the entity in the [`TemplateContext`]. It will also /// spawn all of this [`ResolvedScene`]'s related entities. /// - /// If this [`ResolvedScene`] inherits from another scene, that scene will be applied _first_. + /// If this [`ResolvedScene`] includes a cached scene, that scene will be applied _first_. /// /// This will call `writer_ops` right before calling [`BundleWriter::write`]. This will pass in the `context` value, /// which is the same context used to write all of the scene components to the [`BundleWriter`]. This ensures that @@ -231,36 +231,36 @@ impl ResolvedScene { .entity_references .set(entity_reference, context.entity.id()); } - if let Some(inherited) = &self.inherited { + if let Some(cached) = &self.cached { let scene_patches = context.resource::>(); - let Some(patch) = scene_patches.get(&inherited.handle) else { - return Err(ApplySceneError::MissingInheritedScene { - path: inherited.handle.path().cloned(), - id: inherited.handle.id(), + let Some(patch) = scene_patches.get(&cached.handle) else { + return Err(ApplySceneError::MissingCachedScene { + path: cached.handle.path().cloned(), + id: cached.handle.id(), }); }; - let Some(resolved_inherited) = &patch.resolved else { - return Err(ApplySceneError::UnresolvedInheritedScene { - path: inherited.handle.path().cloned(), - id: inherited.handle.id(), + let Some(resolved_cached) = &patch.resolved else { + return Err(ApplySceneError::UnresolvedCachedScene { + path: cached.handle.path().cloned(), + id: cached.handle.id(), }); }; - let resolved_inherited = resolved_inherited.clone(); + let resolved_cached = resolved_cached.clone(); // SAFETY: bundle_writer is used with the same World across all template.apply calls, // and the next bundle_writer.write call unsafe { - resolved_inherited + resolved_cached .scene .apply_templates_without_bundle_write( context, &mut bundle_writer, // this will skip building / inserting templates that // have local copies in the current scene - // (inherited templates are copy-on-write)() - &inherited.duplicate_templates, + // (cached templates are copy-on-write)() + &cached.duplicate_templates, ) - .map_err(|e| ApplySceneError::InheritedSceneApplyError { - inherited: inherited.handle.path().cloned(), + .map_err(|e| ApplySceneError::CachedSceneApplyError { + cached: cached.handle.path().cloned(), error: Box::new(e), })?; self.apply_templates_without_bundle_write(context, &mut bundle_writer, ())?; @@ -281,7 +281,7 @@ impl ResolvedScene { bundle_writer.write(context.entity); - resolved_inherited + resolved_cached .scene .apply_related(context, bundle_scratch)?; self.apply_related(context, bundle_scratch)?; @@ -401,8 +401,8 @@ impl ResolvedScene { /// This will get the [`Template`], if it already exists in this [`ResolvedScene`]. If it doesn't exist, /// it will use [`Default`] to create a new [`Template`]. /// - /// This uses "copy-on-write" behavior for inherited scenes. If a [`Template`] that the inherited scene has is requested, it will be - /// cloned (using [`Template::clone_template`]), added to the current [`ResolvedScene`], and returned. + /// This uses "copy-on-write" behavior for cached scenes. If a [`Template`] is requested which the cached scene has as well, + /// it will be cloned (using [`Template::clone_template`]), added to the current [`ResolvedScene`], and returned. /// /// This will ignore [`Template`]s added to this scene using [`ResolvedScene::push_template`], as these are not registered as the "canonical" /// [`Template`] for a given [`TypeId`]. @@ -425,8 +425,8 @@ impl ResolvedScene { /// it will use the `default` function to create a new [`ErasedComponentTemplate`]. _For correctness, the [`TypeId`] of the [`Template`] returned /// by `default` should match the passed in `type_id`_. /// - /// This uses "copy-on-write" behavior for inherited scenes. If a [`Template`] that the inherited scene has is requested, it will be - /// cloned (using [`Template::clone_template`]), added to the current [`ResolvedScene`], and returned. + /// This uses "copy-on-write" behavior for cached scenes. If a [`Template`] is requested which the cached scene has as well, + /// it will be cloned (using [`Template::clone_template`]), added to the current [`ResolvedScene`], and returned. /// /// This will ignore [`Template`]s added to this scene using [`ResolvedScene::push_template`], as these are not registered as the "canonical" /// [`Template`] for a given [`TypeId`]. @@ -436,16 +436,16 @@ impl ResolvedScene { type_id: TypeId, default: fn() -> Box, ) -> &'a mut dyn ErasedComponentTemplate { - let mut is_inherited = false; + let mut is_cached = false; let index = self.template_indices.entry(type_id).or_insert_with(|| { let index = self.component_templates.len(); - let value = if let Some(inherited_patch) = &mut context.inherited - && let Some(resolved_inherited) = &inherited_patch.resolved - && let Some(inherited_template) = - resolved_inherited.scene.get_direct_erased_template(type_id) + let value = if let Some(cached_patch) = &mut context.cached + && let Some(resolved_cached) = &cached_patch.resolved + && let Some(cached_template) = + resolved_cached.scene.get_direct_erased_template(type_id) { - is_inherited = true; - inherited_template.clone_template() + is_cached = true; + cached_template.clone_template() } else { default() }; @@ -458,8 +458,8 @@ impl ResolvedScene { .map(|value| &mut **value) .unwrap(); - if is_inherited { - self.inherited + if is_cached { + self.cached .as_mut() .unwrap() .duplicate_templates @@ -469,7 +469,7 @@ impl ResolvedScene { template } - /// Returns the [`ErasedComponentTemplate`] for the given `type_id`, if it exists in this [`ResolvedScene`]. This ignores scene inheritance. + /// Returns the [`ErasedComponentTemplate`] for the given `type_id`, if it exists in this [`ResolvedScene`]. This ignores cached scenes. pub fn get_direct_erased_template( &self, type_id: TypeId, @@ -514,24 +514,24 @@ impl ResolvedScene { .or_insert_with(RelatedResolvedScenes::new::) } - /// Configures this [`ResolvedScene`] to inherit from the given [`ScenePatch`]. + /// Configures this [`ResolvedScene`] to include the given [`ScenePatch`] cached. /// - /// If this [`ResolvedScene`] already inherits from a scene, it will return [`InheritSceneError::MultipleInheritance`]. - /// If this [`ResolvedScene`] already has [`Template`]s or related scenes, it will return [`InheritSceneError::LateInheritance`]. - pub fn inherit(&mut self, handle: Handle) -> Result<(), InheritSceneError> { - if let Some(inherited) = &self.inherited { - return Err(InheritSceneError::MultipleInheritance { - id: inherited.handle.id().untyped(), - path: inherited.handle.path().cloned(), + /// If this [`ResolvedScene`] already includes a cached scene, it will return [`CachedSceneError::MultipleCached`]. + /// If this [`ResolvedScene`] already has [`Template`]s or related scenes, it will return [`CachedSceneError::LateCached`]. + pub fn include_cached(&mut self, handle: Handle) -> Result<(), CachedSceneError> { + if let Some(cached) = &self.cached { + return Err(CachedSceneError::MultipleCached { + id: cached.handle.id().untyped(), + path: cached.handle.path().cloned(), }); } if !(self.component_templates.is_empty() && self.related.is_empty()) { - return Err(InheritSceneError::LateInheritance { + return Err(CachedSceneError::LateCached { id: handle.id().untyped(), path: handle.path().cloned(), }); } - self.inherited = Some(InheritedSceneInfo { + self.cached = Some(CachedSceneInfo { handle, duplicate_templates: HashSet::default(), }); @@ -539,34 +539,36 @@ impl ResolvedScene { } } -/// Information about a [`ResolvedScene`]'s inherited scene. +/// Information about a [`ResolvedScene`]'s cached scene. #[derive(Debug)] -pub(crate) struct InheritedSceneInfo { - /// The handle of the inherited scene. +pub(crate) struct CachedSceneInfo { + /// The handle of the cached scene. pub(crate) handle: Handle, - /// Template types that occur in _both_ the current scene and its inherited scene. - /// This is used to skip insertion of these types when applying the inherited + /// Template types that occur in _both_ the current scene and its cached scene. + /// This is used to skip insertion of these types when applying the cached /// resolved scene. pub(crate) duplicate_templates: HashSet, } -/// The error returned by [`ResolvedScene::inherit`]. +/// The error returned by [`ResolvedScene::include_cached`]. #[derive(Error, Debug)] -pub enum InheritSceneError { - /// Caused when attempting to inherit from a second scene. - #[error("Attempted to inherit from a second scene (id {id:?}, path: {path:?}), which is not allowed.")] - MultipleInheritance { - /// The asset id of the second inherited scene. +pub enum CachedSceneError { + /// Caused when attempting to include a second cached scene. + #[error( + "Attempted to include a second cached scene (id {id:?}, path: {path:?}), which is not allowed." + )] + MultipleCached { + /// The asset id of the second cached scene. id: UntypedAssetId, - /// The path of the second inherited scene. + /// The path of the second cached scene. path: Option>, }, - /// Caused when attempting to inherit when a [`ResolvedScene`] already has [`Template`]s or related scenes. - #[error("Attempted to inherit from (id {id:?}, path: {path:?}), but the resolved scene already has templates. For correctness, inheritance should always come first.")] - LateInheritance { - /// The asset id of the scene that was inherited late. + /// Caused when attempting to include a cached scene when a [`ResolvedScene`] already has [`Template`]s or related scenes. + #[error("Attempted to include cached scene (id {id:?}, path: {path:?}), but the resolved scene already has templates. For correctness, the cached scene should always be included first.")] + LateCached { + /// The asset id of the cached scene that was included late. id: UntypedAssetId, - /// The path of the scene that was inherited late. + /// The path of the cached scene that was included late. path: Option>, }, } @@ -577,28 +579,28 @@ pub enum ApplySceneError { /// Caused when a [`Template`] fails to build #[error("Failed to build a Template in the current Scene: {0}")] TemplateBuildError(BevyError), - /// Caused when the inherited [`ResolvedScene`] fails to apply a [`ResolvedScene`]. - #[error("Failed to apply the inherited Scene (asset path: \"{inherited:?}\"): {error}")] - InheritedSceneApplyError { - /// The asset path of the inherited scene that failed to apply. - inherited: Option>, - /// The error that occurred while applying the inherited scene. + /// Caused when the cached [`ResolvedScene`] fails to apply a [`ResolvedScene`]. + #[error("Failed to apply the cached Scene (asset path: \"{cached:?}\"): {error}")] + CachedSceneApplyError { + /// The asset path of the cached scene that failed to apply. + cached: Option>, + /// The error that occurred while applying the cached scene. error: Box, }, - /// Caused when an inherited scene is not present. - #[error("The inherited scene (id: {id:?}, path: \"{path:?}\") does not exist.")] - MissingInheritedScene { - /// The path of the inherited scene. + /// Caused when an cached scene is not present. + #[error("The cached scene (id: {id:?}, path: \"{path:?}\") does not exist.")] + MissingCachedScene { + /// The path of the cached scene. path: Option>, - /// The asset id of the inherited scene. + /// The asset id of the cached scene. id: AssetId, }, - /// Caused when an inherited scene has not been resolved yet. - #[error("The inherited scene (id: {id:?}, path: \"{path:?}\") has not been resolved yet.")] - UnresolvedInheritedScene { - /// The path of the inherited scene. + /// Caused when an cached scene has not been resolved yet. + #[error("The cached scene (id: {id:?}, path: \"{path:?}\") has not been resolved yet.")] + UnresolvedCachedScene { + /// The path of the cached scene. path: Option>, - /// The asset id of the inherited scene. + /// The asset id of the cached scene. id: AssetId, }, /// Caused when a related [`ResolvedScene`] fails to apply. diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 2c31b073f7577..c3585bd39e738 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,4 +1,4 @@ -use crate::{InheritSceneError, ResolvedScene, SceneList, ScenePatch}; +use crate::{CachedSceneError, ResolvedScene, SceneList, ScenePatch}; use bevy_asset::{Asset, AssetPath, AssetServer, Assets}; use bevy_ecs::{ bundle::Bundle, @@ -17,7 +17,7 @@ use variadics_please::all_tuples; /// Conceptually, a [`Scene`] describes what a spawned [`Entity`] should look like. This often describes what [`Component`]s the entity should have. /// /// [`Scene`] is _always_ a single top level [`Entity`] / root entity. For "lists of scenes" / multiple "root" entities, see [`SceneList`]. These are -/// separate traits for logical reasons: [`World::spawn`] is a "single entity" action. Additionally, "scene inheritance" only makes sense when both scenes +/// separate traits for logical reasons: [`World::spawn`] is a "single entity" action. Additionally, "scene caching" only makes sense when both scenes /// are "single root entities". A good way to think of this is [`Entity`] vs [`Vec`]: these are different types with different APIs and semantics. /// /// ## Resolving Scenes @@ -31,7 +31,7 @@ use variadics_please::all_tuples; /// - Editing an existing [`Template`] (ex: "patching" [`Template`] fields) /// - Adding one or more "related" [`ResolvedScene`]s, which will be spawned alongside the root [`ResolvedScene`] and "related" back to it with a [`Relationship`]. /// - Editing an existing "related" [`ResolvedScene`]. -/// - Setting a [`ScenePatch`] to inherit from. +/// - Setting a [`ScenePatch`] containing a cached [`ResolvedScene`] to apply first. /// /// See [`ResolvedScene`] for more information on how it can be composed. /// @@ -158,10 +158,10 @@ pub enum ResolveSceneError { /// Caused when a dependency listed in [`Scene::register_dependencies`] is not available when calling [`Scene::resolve`] #[error("Cannot resolve scene because the asset dependency {0} is not present. This could be because it isn't loaded yet, or because the asset does not exist. Consider using `queue_spawn_scene()` if you would like to wait for scene dependencies before spawning.")] MissingSceneDependency(AssetPath<'static>), - /// Caused when inheriting a scene during [`Scene::resolve`] fails. + /// Caused when including a cached scene during [`Scene::resolve`] fails. #[error(transparent)] - InheritSceneError(#[from] InheritSceneError), - /// Caused when a Scene/SceneList is not present on the scene asset. + CachedSceneError(#[from] CachedSceneError), + /// Caused when a [`Scene`]/[`SceneList`] is not present on the scene asset. #[error("The Scene/SceneList is not present on the scene asset. This is likely because the scene has already been resolved, which consumed the source scene")] MissingScene, } @@ -172,8 +172,8 @@ pub struct ResolveContext<'a> { pub assets: &'a AssetServer, /// The current [`ScenePatch`] asset collection pub patches: &'a Assets, - /// The currently inherited [`ScenePatch`], if there is one. - pub inherited: Option<&'a ScenePatch>, + /// The currently cached [`ScenePatch`], if there is one. + pub cached: Option<&'a ScenePatch>, } macro_rules! scene_impl { @@ -341,25 +341,25 @@ impl Scene for RelatedScenes { } } -/// A [`Scene`] that will inherit from the [`ScenePatch`] stored at the given [`AssetPath`]. -/// This will _not_ resolve the inherited scene directly on top of this [`ResolvedScene`]. Instead -/// it will set [`ResolvedScene::inherit`], which (when spawning the [`ResolvedScene`]) will apply the inherited [`ResolvedScene`] +/// A [`Scene`] that will include the cached [`ScenePatch`] stored at the given [`AssetPath`]. +/// This will _not_ resolve the cached scene directly on top of this [`ResolvedScene`]. Instead +/// it will set [`ResolvedScene::include_cached`], which (when spawning the [`ResolvedScene`]) will apply the cached [`ResolvedScene`] /// first. _Then_ the top-level [`ResolvedScene`] will be applied. /// -/// This also enables copy-on-write semantics for all future [`Template`] accesses. See [`ResolvedScene`] for more info on "inheritance". +/// This also enables copy-on-write semantics for all future [`Template`] accesses. See [`ResolvedScene`] for more info on caching. #[derive(Clone)] -pub struct InheritSceneAsset( - /// The [`AssetPath`] of the [`ScenePatch`] to inherit from. +pub struct CachedSceneAsset( + /// The [`AssetPath`] of the [`ScenePatch`] to include. pub AssetPath<'static>, ); -impl>> From for InheritSceneAsset { +impl>> From for CachedSceneAsset { fn from(value: I) -> Self { - InheritSceneAsset(value.into()) + CachedSceneAsset(value.into()) } } -impl Scene for InheritSceneAsset { +impl Scene for CachedSceneAsset { fn resolve( self, context: &mut ResolveContext, @@ -368,8 +368,8 @@ impl Scene for InheritSceneAsset { if let Some(handle) = context.assets.get_handle::(&self.0) && let Some(scene_patch) = context.patches.get(&handle) { - scene.inherit(handle)?; - context.inherited = Some(scene_patch); + scene.include_cached(handle)?; + context.cached = Some(scene_patch); Ok(()) } else { Err(ResolveSceneError::MissingSceneDependency(self.0)) @@ -429,7 +429,7 @@ impl Scene for NameEntityReference { } /// A [`Scene`] that will create a new "entity scope" and fully resolve the given scene `S` on top of the current [`ResolvedScene`] (using that scope). -/// It is not "inherited" or cached. +/// It is not cached. #[must_use] pub struct SceneScope(pub S); @@ -448,7 +448,7 @@ impl Scene for SceneScope { } /// A [`SceneList`] that will create a new "entity scope" and fully resolve the given scene list `L` on top of the current [`Vec`] -/// (using that scope). It is not "inherited" or cached. +/// (using that scope). It is not cached. #[must_use] pub struct SceneListScope(pub L); diff --git a/crates/bevy_scene/src/scene_component.rs b/crates/bevy_scene/src/scene_component.rs index 1915b25dfc8c4..941cfcbd4cd7a 100644 --- a/crates/bevy_scene/src/scene_component.rs +++ b/crates/bevy_scene/src/scene_component.rs @@ -50,7 +50,7 @@ impl SceneComponentInfo { "Entity {} was spawned with the \"scene component\" {}, but without its scene. \ Scene components should not be spawned directly as components. Instead, they \ should be spawned as \"scenes\" using world.spawn_scene or commands.spawn_scene. \ - Scene components should be inherited using `:{}` syntax in BSN.", + Scene components should be included using `@{}` syntax in BSN.", context.entity, component.component_name, component.component_name diff --git a/crates/bevy_scene/src/scene_patch.rs b/crates/bevy_scene/src/scene_patch.rs index 48491289079e0..549ce733445a2 100644 --- a/crates/bevy_scene/src/scene_patch.rs +++ b/crates/bevy_scene/src/scene_patch.rs @@ -25,7 +25,7 @@ pub struct ScenePatch { pub dependencies: Vec, /// The [`ResolvedSceneRoot`], if exists. This is populated after the [`Scene`] has been loaded and resolved // TODO: consider breaking this out to prevent mutating asset events when resolved. Assets as Entities will enable this! - // TODO: This Arc exists to allow nested ResolvedSceneRoot::apply when borrowing inherited ScenePatch assets (see the ResolvedSceneRoot::apply implementation). + // TODO: This Arc exists to allow nested ResolvedSceneRoot::apply when borrowing cached ScenePatch assets (see the ResolvedSceneRoot::apply implementation). pub resolved: Option>, } diff --git a/crates/bevy_scene/src/spawn.rs b/crates/bevy_scene/src/spawn.rs index 37b7f2eaff0ec..efb4d94fbc26e 100644 --- a/crates/bevy_scene/src/spawn.rs +++ b/crates/bevy_scene/src/spawn.rs @@ -19,7 +19,7 @@ pub trait WorldSceneExt { /// /// See [`Scene`] for the features of the scene system (and how to use it). /// - /// If your scene has a dependency that might not be loaded yet (for example, it inherits from a `.bsn` asset file), consider using [`World::queue_spawn_scene`]. + /// If your scene has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`World::queue_spawn_scene`]. /// Note that the `.bsn` file format is not yet released. /// /// ``` @@ -84,7 +84,7 @@ pub trait WorldSceneExt { /// #[derive(Component, Default, Clone)] /// struct Shield; /// - /// // This scene inherits from the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn" + /// // This scene includes the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn" /// // is fully loaded. /// world.queue_spawn_scene(bsn! { /// :"player.bsn" @@ -105,7 +105,7 @@ pub trait WorldSceneExt { /// /// See [`Scene`] for the features of the scene system (and how to use it). /// - /// If your scene list has a dependency that might not be loaded yet (for example, it inherits from a `.bsn` asset file), consider using [`World::queue_spawn_scene_list`]. + /// If your scene list has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`World::queue_spawn_scene_list`]. /// Note that the `.bsn` file format is not yet released. /// /// ``` @@ -166,7 +166,7 @@ pub trait WorldSceneExt { /// Red, /// Blue, /// } - /// // This scene list inherits from the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn" + /// // This scene list includes the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn" /// // is loaded. /// world.queue_spawn_scene_list(bsn_list! [ /// ( @@ -228,7 +228,7 @@ pub trait CommandsSceneExt { /// /// See [`Scene`] for the features of the scene system (and how to use it). /// - /// If your scene has a dependency that might not be loaded yet (for example, it inherits from a `.bsn` asset file), consider using [`Commands::queue_spawn_scene`]. + /// If your scene has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`Commands::queue_spawn_scene`]. /// Note that the `.bsn` file format is not yet released. /// /// ``` @@ -277,7 +277,7 @@ pub trait CommandsSceneExt { /// #[derive(Component, Default, Clone)] /// struct Shield; /// - /// // This scene inherits from the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn" + /// // This scene includes the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn" /// // is fully loaded. /// commands.queue_spawn_scene(bsn! { /// :"player.bsn" @@ -298,7 +298,7 @@ pub trait CommandsSceneExt { /// /// See [`Scene`] for the features of the scene system (and how to use it). /// - /// If your scene list has a dependency that might not be loaded yet (for example, it inherits from a `.bsn` asset file), consider using [`Commands::queue_spawn_scene_list`]. + /// If your scene list has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`Commands::queue_spawn_scene_list`]. /// /// ``` /// # use bevy_scene::prelude::*; @@ -345,7 +345,7 @@ pub trait CommandsSceneExt { /// Blue, /// } /// - /// // This scene list inherits from the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn" + /// // This scene list includes the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn" /// // is loaded. /// commands.queue_spawn_scene_list(bsn_list! [ /// ( @@ -454,7 +454,7 @@ pub trait EntityWorldMutSceneExt { /// /// See [`Scene`] for the features of the scene system (and how to use it). /// - /// If your scene has a dependency that might not be loaded yet (for example, it inherits from a `.bsn` asset file), consider using [`World::queue_spawn_scene`]. + /// If your scene has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`World::queue_spawn_scene`]. /// Note that the .bsn file format is not yet released. fn apply_scene(&mut self, scene: S) -> Result<(), SpawnSceneError>; @@ -554,7 +554,7 @@ pub trait EntityCommandsSceneExt { /// /// See [`Scene`] for the features of the scene system (and how to use it). /// - /// If your scene has a dependency that might not be loaded yet (for example, it inherits from a `.bsn` asset file), consider using [`Commands::spawn_scene`]. + /// If your scene has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`Commands::spawn_scene`]. /// Note that the .bsn file format is not yet released. fn apply_scene(&mut self, scene: S) -> &mut Self; diff --git a/examples/large_scenes/bevy_city/src/settings.rs b/examples/large_scenes/bevy_city/src/settings.rs index 35d32b411d603..ac7cb38f22f01 100644 --- a/examples/large_scenes/bevy_city/src/settings.rs +++ b/examples/large_scenes/bevy_city/src/settings.rs @@ -63,7 +63,7 @@ pub fn settings_ui() -> impl Scene { Children [ Text("Settings"), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("Simulate Cars") ThemedText }} } Checked @@ -73,7 +73,7 @@ pub fn settings_ui() -> impl Scene { }) ), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("Shadow maps enabled") ThemedText }} } Checked @@ -91,7 +91,7 @@ pub fn settings_ui() -> impl Scene { ) ), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("Contact shadows enabled") ThemedText }} } Checked @@ -109,7 +109,7 @@ pub fn settings_ui() -> impl Scene { ) ), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("Wireframe Enabled") ThemedText }} } on(checkbox_self_update) @@ -123,7 +123,7 @@ pub fn settings_ui() -> impl Scene { ) ), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("CPU culling") ThemedText }} } Checked @@ -146,7 +146,7 @@ pub fn settings_ui() -> impl Scene { ) ), ( - :FeathersButton { + @FeathersButton { @caption: {bsn! { Text("Regenerate City") ThemedText }} } on( diff --git a/examples/ui/widgets/feathers_counter.rs b/examples/ui/widgets/feathers_counter.rs index 6126eff28eb4c..639a9e7dd25de 100644 --- a/examples/ui/widgets/feathers_counter.rs +++ b/examples/ui/widgets/feathers_counter.rs @@ -58,7 +58,7 @@ fn demo_root() -> impl Scene { } Children [ ( - :FeathersButton + @FeathersButton on(|_activate: On, mut counter: ResMut| { counter.0 -= 1; }) @@ -71,7 +71,7 @@ fn demo_root() -> impl Scene { Text("0") ThemedText CounterText ), ( - :FeathersButton + @FeathersButton on(|_activate: On, mut counter: ResMut| { counter.0 += 1; }) diff --git a/examples/ui/widgets/feathers_gallery.rs b/examples/ui/widgets/feathers_gallery.rs index 2c05e850f7519..873c4e4c2d20e 100644 --- a/examples/ui/widgets/feathers_gallery.rs +++ b/examples/ui/widgets/feathers_gallery.rs @@ -122,7 +122,7 @@ fn demo_column_1() -> impl Scene { } Children [ ( - :FeathersButton { + @FeathersButton { @caption: {bsn! { Text("Normal") ThemedText }} } Node { @@ -135,7 +135,7 @@ fn demo_column_1() -> impl Scene { AutoFocus ), ( - :FeathersButton { + @FeathersButton { @caption: {bsn! { Text("Disabled") ThemedText }}, } Node { @@ -149,7 +149,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersButton { + @FeathersButton { @caption: {bsn! { Text("Primary") ThemedText }}, @variant: ButtonVariant::Primary, } @@ -162,10 +162,10 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersMenu + @FeathersMenu Children [ ( - :FeathersMenuButton { + @FeathersMenuButton { @caption: {bsn! { Text("Menu") ThemedText }} } AccessibleLabel("Menu Example") @@ -174,10 +174,10 @@ fn demo_column_1() -> impl Scene { } ), ( - :FeathersMenuPopup + @FeathersMenuPopup Children [ ( - :FeathersMenuItem { + @FeathersMenuItem { @caption: {bsn! { Text("MenuItem 1") ThemedText }} } on(|_: On| { @@ -185,16 +185,16 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersMenuItem { + @FeathersMenuItem { @caption: {bsn! { Text("MenuItem 2") ThemedText }} } on(|_: On| { info!("Menu item 2 clicked!"); }) ), - :FeathersMenuDivider, + @FeathersMenuDivider, ( - :FeathersMenuItem { + @FeathersMenuItem { @caption: {bsn! { Text("MenuItem 3") ThemedText }} } on(|_: On| { @@ -217,7 +217,7 @@ fn demo_column_1() -> impl Scene { } Children [ ( - :FeathersButton { + @FeathersButton { @caption: {bsn! { Text("Left") ThemedText }}, @corners: RoundedCorners::Left, } @@ -230,7 +230,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersButton { + @FeathersButton { @caption: {bsn! { Text("Center") ThemedText }}, @corners: RoundedCorners::None, } @@ -243,7 +243,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersButton { + @FeathersButton { @caption: {bsn! { Text("Right") ThemedText }}, @variant: ButtonVariant::Primary, @corners: RoundedCorners::Right, @@ -259,7 +259,7 @@ fn demo_column_1() -> impl Scene { ] ), ( - :FeathersButton + @FeathersButton on(|_activate: On, mut ovr: ResMut| { ovr.0 = if ovr.0.is_some() { None @@ -271,7 +271,7 @@ fn demo_column_1() -> impl Scene { Children [ (Text("Toggle override") ThemedText) ] ), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("Checkbox") ThemedText }} } Checked @@ -297,7 +297,7 @@ fn demo_column_1() -> impl Scene { ) ), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("Fast Click Checkbox") ThemedText }} } ActivateOnPress @@ -316,7 +316,7 @@ fn demo_column_1() -> impl Scene { ) ), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("Disabled") ThemedText }}, } InteractionDisabled @@ -326,7 +326,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("Checked+Disabled") ThemedText }} } InteractionDisabled @@ -355,22 +355,22 @@ fn demo_column_1() -> impl Scene { on(radio_self_update) Children [ ( - :FeathersRadio { + @FeathersRadio { @caption: {bsn! { Text("One") ThemedText }} } Checked ), - :FeathersRadio { + @FeathersRadio { @caption: {bsn! { Text("Two") ThemedText }} }, ( - :FeathersRadio { + @FeathersRadio { @caption: {bsn! { Text("Fast Click") ThemedText }} } ActivateOnPress ), ( - :FeathersRadio { + @FeathersRadio { @caption: {bsn! { Text("Disabled") ThemedText }} } InteractionDisabled @@ -388,15 +388,15 @@ fn demo_column_1() -> impl Scene { column_gap: px(8), } Children [ - (:FeathersToggleSwitch on(checkbox_self_update)), - (:FeathersToggleSwitch ActivateOnPress on(checkbox_self_update)), - (:FeathersToggleSwitch InteractionDisabled on(checkbox_self_update)), - (:FeathersToggleSwitch InteractionDisabled Checked on(checkbox_self_update)), - (:FeathersDisclosureToggle on(checkbox_self_update)), + (@FeathersToggleSwitch on(checkbox_self_update)), + (@FeathersToggleSwitch ActivateOnPress on(checkbox_self_update)), + (@FeathersToggleSwitch InteractionDisabled on(checkbox_self_update)), + (@FeathersToggleSwitch InteractionDisabled Checked on(checkbox_self_update)), + (@FeathersDisclosureToggle on(checkbox_self_update)), ] ), ( - :FeathersSlider { + @FeathersSlider { @max: 100.0, @value: 20.0, } @@ -413,19 +413,19 @@ fn demo_column_1() -> impl Scene { column_gap: px(4), } Children [ - :label("Srgba"), + label("Srgba"), // Spacer :flex_spacer, // Text input ( - :FeathersTextInputContainer + @FeathersTextInputContainer Node { flex_grow: 0. padding: { px(4).left() }, } Children [ ( - :FeathersTextInput { + @FeathersTextInput { @visible_width: 10f32, @max_characters: 9usize, } @@ -437,18 +437,18 @@ fn demo_column_1() -> impl Scene { ) ] ) - (:FeathersColorSwatch SwatchType::Rgb), + (@FeathersColorSwatch SwatchType::Rgb), ] ), ( - :FeathersColorPlane::RedBlue + @FeathersColorPlane::RedBlue on(|change: On>, mut color: ResMut| { color.rgb_color.red = change.value.x; color.rgb_color.blue = change.value.y; }) ), ( - :FeathersColorSlider { + @FeathersColorSlider { @value: 0.5, @channel: ColorChannel::Red } @@ -458,7 +458,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersColorSlider { + @FeathersColorSlider { @value: 0.5, @channel: ColorChannel::Green } @@ -468,7 +468,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersColorSlider { + @FeathersColorSlider { @value: 0.5, @channel: ColorChannel::Blue } @@ -478,7 +478,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersColorSlider { + @FeathersColorSlider { @value: 0.5, @channel: ColorChannel::Alpha } @@ -495,12 +495,12 @@ fn demo_column_1() -> impl Scene { justify_content: JustifyContent::SpaceBetween, } Children [ - :label("Hsl"), - (:FeathersColorSwatch SwatchType::Hsl) + label("Hsl"), + (@FeathersColorSwatch SwatchType::Hsl) ] ), ( - :FeathersColorSlider { + @FeathersColorSlider { @value: 0.5, @channel: ColorChannel::HslHue } @@ -510,7 +510,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersColorSlider { + @FeathersColorSlider { @value: 0.5, @channel: ColorChannel::HslSaturation } @@ -520,7 +520,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersColorSlider { + @FeathersColorSlider { @value: 0.5, @channel: ColorChannel::HslLightness } @@ -549,43 +549,43 @@ fn demo_column_2() -> impl Scene { ( :pane Children [ :pane_header Children [ - :FeathersToolButton { + @FeathersToolButton { @variant: ButtonVariant::Primary, } Children [ (Text("\u{0398}") ThemedText) ], :pane_header_divider, - :FeathersToolButton { + @FeathersToolButton { @variant: ButtonVariant::Plain, } Children [ (Text("\u{00BC}") ThemedText) ], - :FeathersToolButton { + @FeathersToolButton { @variant: ButtonVariant::Plain, } Children [ (Text("\u{00BD}") ThemedText) ], - :FeathersToolButton { + @FeathersToolButton { @variant: ButtonVariant::Plain, } Children [ (Text("\u{00BE}") ThemedText) ], :pane_header_divider, - :FeathersToolButton { + @FeathersToolButton { @variant: ButtonVariant::Plain, } Children [ - :icon(icons::CHEVRON_DOWN) + icon(icons::CHEVRON_DOWN) ], :flex_spacer, - :FeathersToolButton { + @FeathersToolButton { @variant: ButtonVariant::Plain, } Children [ - :icon(icons::X) + icon(icons::X) ], ], ( :pane_body Children [ - :label_dim("A standard editor pane"), + label_dim("A standard editor pane"), :subpane Children [ :subpane_header Children [ (Text("Left") ThemedText), @@ -593,7 +593,7 @@ fn demo_column_2() -> impl Scene { (Text("Right") ThemedText) ], :subpane_body Children [ - :label_dim("A standard sub-pane"), + label_dim("A standard sub-pane"), :group Children [ :group_header Children [ @@ -601,10 +601,10 @@ fn demo_column_2() -> impl Scene { ], :group_body Children [ - :label("A standard group"), - :label_small("Scalar property"), + label("A standard group"), + label_small("Scalar property"), ( - :FeathersNumberInput + :@FeathersNumberInput DemoScalarField Node { flex_grow: 1.0, @@ -618,9 +618,9 @@ fn demo_column_2() -> impl Scene { } }) ), - :label_small("Scalar property (copy)"), + label_small("Scalar property (copy)"), ( - :FeathersNumberInput + :@FeathersNumberInput DemoScalarField Node { flex_grow: 1.0, @@ -634,7 +634,7 @@ fn demo_column_2() -> impl Scene { } }) ), - :label_small("Vec3 property"), + label_small("Vec3 property"), Node { display: Display::Flex, flex_direction: FlexDirection::Row, @@ -644,7 +644,7 @@ fn demo_column_2() -> impl Scene { } Children [ ( - :FeathersNumberInput { + @FeathersNumberInput { @sigil_color: tokens::TEXT_INPUT_X_AXIS, @label_text: "X", } @@ -662,7 +662,7 @@ fn demo_column_2() -> impl Scene { }) ), ( - :FeathersNumberInput { + @FeathersNumberInput { @sigil_color: tokens::TEXT_INPUT_Y_AXIS, @label_text: "Y", } @@ -679,7 +679,7 @@ fn demo_column_2() -> impl Scene { }) ), ( - :FeathersNumberInput { + @FeathersNumberInput { @sigil_color: tokens::TEXT_INPUT_Z_AXIS, @label_text: "Z", } diff --git a/examples/ui/widgets/virtual_keyboard.rs b/examples/ui/widgets/virtual_keyboard.rs index ebd07d2ab0103..57b1d894a29e8 100644 --- a/examples/ui/widgets/virtual_keyboard.rs +++ b/examples/ui/widgets/virtual_keyboard.rs @@ -59,7 +59,7 @@ fn keyboard() -> impl Scene { Children [ Text("virtual keyboard"), ( - :VirtualKeyboard::<&str> { @keys: keys } + @VirtualKeyboard::<&str> { @keys: keys } on(on_virtual_key_pressed) ) ]