From a3c0e7e806664fe9352056541d2f435652dc3822 Mon Sep 17 00:00:00 2001 From: laund Date: Wed, 20 May 2026 02:32:55 +0200 Subject: [PATCH 1/8] Initial broken state moving over to @SceneComponent --- crates/bevy_scene/macros/src/bsn/codegen.rs | 106 +++++++++++--------- crates/bevy_scene/macros/src/bsn/parse.rs | 70 +++++++------ crates/bevy_scene/macros/src/bsn/types.rs | 9 +- crates/bevy_scene/src/lib.rs | 16 +-- 4 files changed, 110 insertions(+), 91 deletions(-) diff --git a/crates/bevy_scene/macros/src/bsn/codegen.rs b/crates/bevy_scene/macros/src/bsn/codegen.rs index 66861e0a90416..3af97ea545a61 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::InheritSceneAsset::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( diff --git a/crates/bevy_scene/macros/src/bsn/parse.rs b/crates/bevy_scene/macros/src/bsn/parse.rs index 2e8190c079580..39515996356ba 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) { @@ -103,11 +103,14 @@ impl BsnEntry { BsnEntry::Name(input.parse::()?) } } else if input.peek(Brace) { - BsnEntry::SceneExpression(braced_tokens(input)?) + BsnEntry::UncachedScene(BsnScene::parse(input, found_cached_scene)?) + } else if input.peek(At) { + input.parse::()?; + BsnEntry::UncachedScene(BsnScene::SceneComponent(input.parse::()?)) } 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 +171,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,30 +234,41 @@ impl Parse for BsnSceneFnArg { } } } -impl BsnInheritedScene { - fn parse(input: ParseStream, found_inherited_scene: bool) -> Result { +impl BsnScene { + fn parse(input: ParseStream, found_cached_scene: bool) -> Result { let colon = input.parse::()?; - if found_inherited_scene { + if found_cached_scene { return Err(syn::Error::new( colon.span(), - "Cannot inherit scenes more than once", + "Cannot cache scenes more than once", )); } Ok(if input.peek(LitStr) { let path = input.parse::()?; - BsnInheritedScene::Asset(path) + BsnScene::Asset(path) } else if input.peek(Brace) { - BsnInheritedScene::Expression(braced_tokens(input)?) + BsnScene::Expression(braced_tokens(input)?) + } else if input.peek(At) { + input.parse::()?; + BsnScene::SceneComponent(input.parse::()?) } 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 externally 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 { + BsnScene::Fn(BsnSceneFn { path, args: input.parse()?, }) @@ -263,7 +277,7 @@ impl BsnInheritedScene { 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..33cdb760e8143 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), } diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs index e92ef5c223582..2172811332b75 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -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::()); } @@ -1916,7 +1916,7 @@ mod tests { let scene = bsn! { #Name Children [ - :Widget{ + :@Widget{ entity: #Name } ] @@ -2017,7 +2017,7 @@ mod tests { let prop_expr = bsn! { Children [ - :Widget { + :@Widget { @entity: Entity::PLACEHOLDER } ] @@ -2030,7 +2030,7 @@ mod tests { let scene_prop = bsn! { #Name Children [ - :Widget { + :@Widget { @entity: #Name } ] From 1b573efe1f2981468828dd524d052c040e826c92 Mon Sep 17 00:00:00 2001 From: laund Date: Wed, 20 May 2026 16:50:17 +0200 Subject: [PATCH 2/8] fix parsing --- crates/bevy_scene/macros/src/bsn/parse.rs | 41 +++++++++++++++++------ crates/bevy_scene/macros/src/bsn/types.rs | 8 +++++ crates/bevy_scene/src/lib.rs | 24 ++++++------- 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/crates/bevy_scene/macros/src/bsn/parse.rs b/crates/bevy_scene/macros/src/bsn/parse.rs index 39515996356ba..e2bd79f7ec846 100644 --- a/crates/bevy_scene/macros/src/bsn/parse.rs +++ b/crates/bevy_scene/macros/src/bsn/parse.rs @@ -105,8 +105,7 @@ impl BsnEntry { } else if input.peek(Brace) { BsnEntry::UncachedScene(BsnScene::parse(input, found_cached_scene)?) } else if input.peek(At) { - input.parse::()?; - BsnEntry::UncachedScene(BsnScene::SceneComponent(input.parse::()?)) + BsnEntry::UncachedScene(BsnScene::parse(input, found_cached_scene)?) } else { let is_template = input.peek(Tilde); if is_template { @@ -236,27 +235,43 @@ impl Parse for BsnSceneFnArg { } impl BsnScene { fn parse(input: ParseStream, found_cached_scene: bool) -> Result { - let colon = input.parse::()?; + let cached = if input.peek(Token![:]) { + Some(input.parse::()?) + } else { + None + }; + + let err_cached = |msg: &str| { + if let Some(colon) = cached { + Err(syn::Error::new(colon.span(), msg)) + } else { + Ok(()) + } + }; + if found_cached_scene { - return Err(syn::Error::new( - colon.span(), - "Cannot cache scenes more than once", - )); + err_cached("Cannot cache scenes more than once")?; } + Ok(if input.peek(LitStr) { let path = input.parse::()?; BsnScene::Asset(path) } else if input.peek(Brace) { + err_cached("Cannot cache scene expressions")?; BsnScene::Expression(braced_tokens(input)?) } else if input.peek(At) { input.parse::()?; - BsnScene::SceneComponent(input.parse::()?) + let sc = input.parse::()?; + if sc.fields.len() > 0 { + err_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 => { - // Scene components are parsed externally if an @ is found. + // 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(), @@ -268,10 +283,14 @@ impl BsnScene { } PathType::Function | PathType::TypeFunction => { let path = input.parse::()?; - BsnScene::Fn(BsnSceneFn { + let func = BsnSceneFn { path, args: input.parse()?, - }) + }; + if func.args.0.is_some() { + err_cached("Cannot cache Scene function with arguments")?; + } + BsnScene::Fn(func) } path_type => { return Err(syn::Error::new( diff --git a/crates/bevy_scene/macros/src/bsn/types.rs b/crates/bevy_scene/macros/src/bsn/types.rs index 33cdb760e8143..bfbf9bf043c43 100644 --- a/crates/bevy_scene/macros/src/bsn/types.rs +++ b/crates/bevy_scene/macros/src/bsn/types.rs @@ -86,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/src/lib.rs b/crates/bevy_scene/src/lib.rs index 2172811332b75..093696c4023e8 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -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 } ] @@ -1947,7 +1947,7 @@ mod tests { let inherit_pass_expr = bsn! { #Name Children [ - :widget(#{Entity::PLACEHOLDER}) + widget(#{Entity::PLACEHOLDER}) ] }; let entity = world.spawn_scene(inherit_pass_expr).unwrap().id(); @@ -1969,7 +1969,7 @@ mod tests { let inherit_pass_name = bsn! { #Name Children [ - :widget(#Name) + widget(#Name) ] }; let entity = world.spawn_scene(inherit_pass_name).unwrap().id(); @@ -2017,7 +2017,7 @@ mod tests { let prop_expr = bsn! { Children [ - :@Widget { + @Widget { @entity: Entity::PLACEHOLDER } ] @@ -2030,7 +2030,7 @@ mod tests { let scene_prop = bsn! { #Name Children [ - :@Widget { + @Widget { @entity: #Name } ] From 6342996704707196cd106a6da24b28cce37cdf38 Mon Sep 17 00:00:00 2001 From: laund Date: Wed, 20 May 2026 20:56:26 +0200 Subject: [PATCH 3/8] Update feathers and replace "inheritance" in types and docstring. both lib.rs files are left out as their docs are undergoing a major rework --- crates/bevy_feathers/src/controls/button.rs | 2 +- .../src/controls/disclosure_toggle.rs | 2 +- crates/bevy_feathers/src/controls/menu.rs | 4 +- .../src/controls/number_input.rs | 4 +- .../src/controls/virtual_keyboard.rs | 2 +- crates/bevy_scene/macros/src/bsn/codegen.rs | 6 +- crates/bevy_scene/macros/src/bsn/parse.rs | 10 +- crates/bevy_scene/macros/src/lib.rs | 4 +- .../bevy_scene/macros/src/scene_component.rs | 2 +- crates/bevy_scene/src/lib.rs | 41 ++--- crates/bevy_scene/src/resolved_scene.rs | 168 +++++++++--------- crates/bevy_scene/src/scene.rs | 40 ++--- crates/bevy_scene/src/scene_component.rs | 2 +- crates/bevy_scene/src/scene_patch.rs | 2 +- crates/bevy_scene/src/spawn.rs | 20 +-- .../large_scenes/bevy_city/src/settings.rs | 12 +- 16 files changed, 153 insertions(+), 168 deletions(-) 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 3af97ea545a61..df9ab3fbb614b 100644 --- a/crates/bevy_scene/macros/src/bsn/codegen.rs +++ b/crates/bevy_scene/macros/src/bsn/codegen.rs @@ -282,7 +282,7 @@ impl BsnScene { let bevy_scene = ctx.bevy_scene; match self { BsnScene::Asset(lit) => Ok(quote! { - #bevy_scene::InheritSceneAsset::from(#lit) + #bevy_scene::CachedSceneAsset::from(#lit) }), BsnScene::Fn(func) => Ok(func.to_tokens(ctx)), BsnScene::SceneComponent(bsn_type) => { @@ -453,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 e2bd79f7ec846..6cae583a5ab79 100644 --- a/crates/bevy_scene/macros/src/bsn/parse.rs +++ b/crates/bevy_scene/macros/src/bsn/parse.rs @@ -102,9 +102,7 @@ impl BsnEntry { } else { BsnEntry::Name(input.parse::()?) } - } else if input.peek(Brace) { - BsnEntry::UncachedScene(BsnScene::parse(input, found_cached_scene)?) - } else if input.peek(At) { + } else if input.peek(Brace) || input.peek(At) { BsnEntry::UncachedScene(BsnScene::parse(input, found_cached_scene)?) } else { let is_template = input.peek(Tilde); @@ -255,6 +253,12 @@ impl BsnScene { Ok(if input.peek(LitStr) { let path = input.parse::()?; + if cached.is_none() { + return Err(syn::Error::new( + path.span(), + "Cannot use scene from asset path without caching, please add : prefix.", + )); + } BsnScene::Asset(path) } else if input.peek(Brace) { err_cached("Cannot cache scene expressions")?; diff --git a/crates/bevy_scene/macros/src/lib.rs b/crates/bevy_scene/macros/src/lib.rs index 61ca6e92ea36c..5a7465f7a2d4c 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. @@ -312,7 +312,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 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 093696c4023e8..b9a88e497d18e 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`), @@ -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 //! @@ -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, @@ -1944,46 +1944,25 @@ mod tests { let mut app = test_app(); let world = app.world_mut(); - let inherit_pass_expr = bsn! { + let pass_expr = bsn! { #Name Children [ widget(#{Entity::PLACEHOLDER}) ] }; - let entity = world.spawn_scene(inherit_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 noninherit_pass_expr = bsn! { - #Name - Children [ - widget(#{Entity::PLACEHOLDER}) - ] - }; - let entity = world.spawn_scene(noninherit_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(); @@ -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..b84e1cabaae13 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`] relies on 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 +/// This uses "copy-on-write" behavior for cached scenes. If a [`Template`] that the cached scene has is requested, 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" cacheing. 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`] relies on 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`] relies on 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,7 +401,7 @@ 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 + /// This uses "copy-on-write" behavior for cached scenes. If a [`Template`] that the cached scene has is requested, 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" @@ -425,7 +425,7 @@ 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 + /// This uses "copy-on-write" behavior for cached scenes. If a [`Template`] that the cached scene has is requested, 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" @@ -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 use 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 relies on 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 use_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::use_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 use a second cached scene. + #[error( + "Attempted to use 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 use a cached scene when a [`ResolvedScene`] already has [`Template`]s or related scenes. + #[error("Attempted to use cached scene (id {id:?}, path: {path:?}), but the resolved scene already has templates. For correctness, cached scenes should always come first.")] + LateCached { + /// The asset id of the scene that was cached late. id: UntypedAssetId, - /// The path of the scene that was inherited late. + /// The path of the scene that was cached 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..831a14c5877c3 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,9 +158,9 @@ 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 using a cached scene during [`Scene::resolve`] fails. #[error(transparent)] - InheritSceneError(#[from] InheritSceneError), + 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 be used from the [`ScenePatch`] stored at the given [`AssetPath`]. +/// This will _not_ resolve the cached scene directly on top of this [`ResolvedScene`]. Instead +/// it will set [`ResolvedScene::use_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 use. 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.use_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" or 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" or 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 a20916a9e35e0..f2277c78bcb88 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 cached 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 e3dd823275813..397bc1f944562 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 composes from a `.bsn` asset file), consider using [`World::queue_spawn_scene`]. /// /// ``` /// # use bevy_app::App; @@ -83,7 +83,7 @@ pub trait WorldSceneExt { /// #[derive(Component, Default, Clone)] /// struct Shield; /// - /// // This scene inherits from the "player.bsn" asset. It will be spawned on the frame that "player.bsn" + /// // This scene pulls in the "player.bsn" asset. It will be spawned on the frame that "player.bsn" /// // is fully loaded. /// world.queue_spawn_scene(bsn! { /// :"player.bsn" @@ -104,7 +104,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 composes from a `.bsn` asset file), consider using [`World::queue_spawn_scene_list`]. /// /// ``` /// # use bevy_app::App; @@ -164,7 +164,7 @@ pub trait WorldSceneExt { /// Red, /// Blue, /// } - /// // This scene list inherits from the "player.bsn" asset. It will be spawned on the frame that "player.bsn" + /// // This scene list composes entities using the "player.bsn" asset. It will be spawned on the frame that "player.bsn" /// // is loaded. /// world.queue_spawn_scene_list(bsn_list! [ /// ( @@ -226,7 +226,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 composes from a `.bsn` asset file), consider using [`Commands::queue_spawn_scene`]. /// /// ``` /// # use bevy_scene::prelude::*; @@ -274,7 +274,7 @@ pub trait CommandsSceneExt { /// #[derive(Component, Default, Clone)] /// struct Shield; /// - /// // This scene inherits from the "player.bsn" asset. It will be spawned on the frame that "player.bsn" + /// // This scene composes from the "player.bsn" asset. It will be spawned on the frame that "player.bsn" /// // is fully loaded. /// commands.queue_spawn_scene(bsn! { /// :"player.bsn" @@ -295,7 +295,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 composes from a `.bsn` asset file), consider using [`Commands::queue_spawn_scene_list`]. /// /// ``` /// # use bevy_scene::prelude::*; @@ -341,7 +341,7 @@ pub trait CommandsSceneExt { /// Blue, /// } /// - /// // This scene list inherits from the "player.bsn" asset. It will be spawned on the frame that "player.bsn" + /// // This scene list composes entities using the "player.bsn" asset. It will be spawned on the frame that "player.bsn" /// // is loaded. /// commands.queue_spawn_scene_list(bsn_list! [ /// ( @@ -450,7 +450,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 composes from a `.bsn` asset file), consider using [`World::queue_spawn_scene`]. fn apply_scene(&mut self, scene: S) -> Result<(), SpawnSceneError>; /// Queues the `scene` to be applied. This will evaluate the `scene`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved and spawned @@ -549,7 +549,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 composes from a `.bsn` asset file), consider using [`Commands::spawn_scene`]. fn apply_scene(&mut self, scene: S) -> &mut Self; /// Queues the `scene` to be applied. This will evaluate the `scene`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved and spawned 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( From 6511fc98be7ac650d0df8dee55b6e09e025b581d Mon Sep 17 00:00:00 2001 From: laund Date: Wed, 20 May 2026 21:11:41 +0200 Subject: [PATCH 4/8] fix all examples (cargo check --workspace --examples) --- examples/ui/widgets/feathers_counter.rs | 4 +- examples/ui/widgets/feathers_gallery.rs | 122 ++++++++++++------------ examples/ui/widgets/virtual_keyboard.rs | 2 +- 3 files changed, 64 insertions(+), 64 deletions(-) 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 f1724b465296b..2f40e96f38593 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 { @@ -134,7 +134,7 @@ fn demo_column_1() -> impl Scene { AutoFocus ), ( - :FeathersButton { + @FeathersButton { @caption: {bsn! { Text("Disabled") ThemedText }}, } Node { @@ -147,7 +147,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersButton { + @FeathersButton { @caption: {bsn! { Text("Primary") ThemedText }}, @variant: ButtonVariant::Primary, } @@ -159,10 +159,10 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersMenu + @FeathersMenu Children [ ( - :FeathersMenuButton { + @FeathersMenuButton { @caption: {bsn! { Text("Menu") ThemedText }} } Node { @@ -170,10 +170,10 @@ fn demo_column_1() -> impl Scene { } ), ( - :FeathersMenuPopup + @FeathersMenuPopup Children [ ( - :FeathersMenuItem { + @FeathersMenuItem { @caption: {bsn! { Text("MenuItem 1") ThemedText }} } on(|_: On| { @@ -181,16 +181,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| { @@ -213,7 +213,7 @@ fn demo_column_1() -> impl Scene { } Children [ ( - :FeathersButton { + @FeathersButton { @caption: {bsn! { Text("Left") ThemedText }}, @corners: RoundedCorners::Left, } @@ -225,7 +225,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersButton { + @FeathersButton { @caption: {bsn! { Text("Center") ThemedText }}, @corners: RoundedCorners::None, } @@ -237,7 +237,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersButton { + @FeathersButton { @caption: {bsn! { Text("Right") ThemedText }}, @variant: ButtonVariant::Primary, @corners: RoundedCorners::Right, @@ -252,7 +252,7 @@ fn demo_column_1() -> impl Scene { ] ), ( - :FeathersButton + @FeathersButton on(|_activate: On, mut ovr: ResMut| { ovr.0 = if ovr.0.is_some() { None @@ -264,7 +264,7 @@ fn demo_column_1() -> impl Scene { Children [ (Text("Toggle override") ThemedText) ] ), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("Checkbox") ThemedText }} } Checked @@ -289,7 +289,7 @@ fn demo_column_1() -> impl Scene { ) ), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("Fast Click Checkbox") ThemedText }} } ActivateOnPress @@ -307,7 +307,7 @@ fn demo_column_1() -> impl Scene { ) ), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("Disabled") ThemedText }}, } InteractionDisabled @@ -316,7 +316,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersCheckbox { + @FeathersCheckbox { @caption: {bsn! { Text("Checked+Disabled") ThemedText }} } InteractionDisabled @@ -344,22 +344,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 @@ -377,15 +377,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, } @@ -402,19 +402,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, } @@ -426,18 +426,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 } @@ -446,7 +446,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersColorSlider { + @FeathersColorSlider { @value: 0.5, @channel: ColorChannel::Green } @@ -455,7 +455,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersColorSlider { + @FeathersColorSlider { @value: 0.5, @channel: ColorChannel::Blue } @@ -464,7 +464,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersColorSlider { + @FeathersColorSlider { @value: 0.5, @channel: ColorChannel::Alpha } @@ -480,12 +480,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 } @@ -494,7 +494,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersColorSlider { + @FeathersColorSlider { @value: 0.5, @channel: ColorChannel::HslSaturation } @@ -503,7 +503,7 @@ fn demo_column_1() -> impl Scene { }) ), ( - :FeathersColorSlider { + @FeathersColorSlider { @value: 0.5, @channel: ColorChannel::HslLightness } @@ -531,43 +531,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), @@ -575,7 +575,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 [ @@ -583,10 +583,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, @@ -600,9 +600,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, @@ -616,7 +616,7 @@ fn demo_column_2() -> impl Scene { } }) ), - :label_small("Vec3 property"), + label_small("Vec3 property"), Node { display: Display::Flex, flex_direction: FlexDirection::Row, @@ -626,7 +626,7 @@ fn demo_column_2() -> impl Scene { } Children [ ( - :FeathersNumberInput { + @FeathersNumberInput { @sigil_color: tokens::TEXT_INPUT_X_AXIS, @label_text: "X", } @@ -644,7 +644,7 @@ fn demo_column_2() -> impl Scene { }) ), ( - :FeathersNumberInput { + @FeathersNumberInput { @sigil_color: tokens::TEXT_INPUT_Y_AXIS, @label_text: "Y", } @@ -661,7 +661,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) ) ] From 126e3812a0718a0ab092a195cc2469268380f554 Mon Sep 17 00:00:00 2001 From: laund Date: Wed, 20 May 2026 23:31:59 +0200 Subject: [PATCH 5/8] Change terminology to "include"/"included", used for what was previously part of "inheritance" --- crates/bevy_scene/src/resolved_scene.rs | 38 ++++++++++++------------ crates/bevy_scene/src/scene.rs | 14 ++++----- crates/bevy_scene/src/scene_component.rs | 2 +- crates/bevy_scene/src/spawn.rs | 20 ++++++------- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/crates/bevy_scene/src/resolved_scene.rs b/crates/bevy_scene/src/resolved_scene.rs index b84e1cabaae13..3814cf29151be 100644 --- a/crates/bevy_scene/src/resolved_scene.rs +++ b/crates/bevy_scene/src/resolved_scene.rs @@ -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`] relies on a cached 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, @@ -153,8 +153,8 @@ impl ResolvedSceneListRoot { /// 2. A collection of [`RelatedResolvedScenes`], which will be spawned as "related" entities (ex: [`Children`] entities). /// 3. An optional cached [`ScenePatch`]. /// -/// This uses "copy-on-write" behavior for cached scenes. If a [`Template`] that the cached 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 cached scene (including its related scenes) is applied _first_. _Then_ this /// [`ResolvedScene`] is applied. @@ -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`] relies on a cached 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`] relies on a cached 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 @@ -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 cached scenes. If a [`Template`] that the cached 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 cached scenes. If a [`Template`] that the cached 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`]. @@ -514,11 +514,11 @@ impl ResolvedScene { .or_insert_with(RelatedResolvedScenes::new::) } - /// Configures this [`ResolvedScene`] to use the given [`ScenePatch`] cached. + /// Configures this [`ResolvedScene`] to include the given [`ScenePatch`] cached. /// - /// If this [`ResolvedScene`] already relies on a cached scene, it will return [`CachedSceneError::MultipleCached`]. + /// 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 use_cached(&mut self, handle: Handle) -> Result<(), CachedSceneError> { + 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(), @@ -550,12 +550,12 @@ pub(crate) struct CachedSceneInfo { pub(crate) duplicate_templates: HashSet, } -/// The error returned by [`ResolvedScene::use_cached`]. +/// The error returned by [`ResolvedScene::include_cached`]. #[derive(Error, Debug)] pub enum CachedSceneError { - /// Caused when attempting to use a second cached scene. + /// Caused when attempting to include a second cached scene. #[error( - "Attempted to use a second cached scene (id {id:?}, path: {path:?}), which is not allowed." + "Attempted to include a second cached scene (id {id:?}, path: {path:?}), which is not allowed." )] MultipleCached { /// The asset id of the second cached scene. @@ -563,12 +563,12 @@ pub enum CachedSceneError { /// The path of the second cached scene. path: Option>, }, - /// Caused when attempting to use a cached scene when a [`ResolvedScene`] already has [`Template`]s or related scenes. - #[error("Attempted to use cached scene (id {id:?}, path: {path:?}), but the resolved scene already has templates. For correctness, cached scenes should always come first.")] + /// 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 scene that was cached late. + /// The asset id of the cached scene that was included late. id: UntypedAssetId, - /// The path of the scene that was cached late. + /// The path of the cached scene that was included late. path: Option>, }, } diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 831a14c5877c3..a268f8ee2eb75 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -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 using a cached scene during [`Scene::resolve`] fails. + /// Caused when including a cached scene during [`Scene::resolve`] fails. #[error(transparent)] CachedSceneError(#[from] CachedSceneError), - /// Caused when a Scene/SceneList is not present on the scene asset. + /// 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, } @@ -341,15 +341,15 @@ impl Scene for RelatedScenes { } } -/// A [`Scene`] that will be used from the [`ScenePatch`] stored at the given [`AssetPath`]. +/// 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::use_cached`], which (when spawning the [`ResolvedScene`]) will apply the cached [`ResolvedScene`] +/// 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 caching. #[derive(Clone)] pub struct CachedSceneAsset( - /// The [`AssetPath`] of the [`ScenePatch`] to use. + /// The [`AssetPath`] of the [`ScenePatch`] to include. pub AssetPath<'static>, ); @@ -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 "cached" 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 "cached" 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 f2277c78bcb88..760fef4229665 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 cached 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/spawn.rs b/crates/bevy_scene/src/spawn.rs index 397bc1f944562..6e8f56d2e3648 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 composes 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`]. /// /// ``` /// # use bevy_app::App; @@ -83,7 +83,7 @@ pub trait WorldSceneExt { /// #[derive(Component, Default, Clone)] /// struct Shield; /// - /// // This scene pulls in the "player.bsn" asset. It will be spawned on the frame that "player.bsn" + /// // This scene includes the "player.bsn" asset. It will be spawned on the frame that "player.bsn" /// // is fully loaded. /// world.queue_spawn_scene(bsn! { /// :"player.bsn" @@ -104,7 +104,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 composes 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`]. /// /// ``` /// # use bevy_app::App; @@ -164,7 +164,7 @@ pub trait WorldSceneExt { /// Red, /// Blue, /// } - /// // This scene list composes entities using the "player.bsn" asset. It will be spawned on the frame that "player.bsn" + /// // This scene list includes the "player.bsn" asset. It will be spawned on the frame that "player.bsn" /// // is loaded. /// world.queue_spawn_scene_list(bsn_list! [ /// ( @@ -226,7 +226,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 composes 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`]. /// /// ``` /// # use bevy_scene::prelude::*; @@ -274,7 +274,7 @@ pub trait CommandsSceneExt { /// #[derive(Component, Default, Clone)] /// struct Shield; /// - /// // This scene composes from the "player.bsn" asset. It will be spawned on the frame that "player.bsn" + /// // This scene includes the "player.bsn" asset. It will be spawned on the frame that "player.bsn" /// // is fully loaded. /// commands.queue_spawn_scene(bsn! { /// :"player.bsn" @@ -295,7 +295,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 composes 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::*; @@ -341,7 +341,7 @@ pub trait CommandsSceneExt { /// Blue, /// } /// - /// // This scene list composes entities using the "player.bsn" asset. It will be spawned on the frame that "player.bsn" + /// // This scene list includes the "player.bsn" asset. It will be spawned on the frame that "player.bsn" /// // is loaded. /// commands.queue_spawn_scene_list(bsn_list! [ /// ( @@ -450,7 +450,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 composes 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`]. fn apply_scene(&mut self, scene: S) -> Result<(), SpawnSceneError>; /// Queues the `scene` to be applied. This will evaluate the `scene`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved and spawned @@ -549,7 +549,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 composes 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`]. fn apply_scene(&mut self, scene: S) -> &mut Self; /// Queues the `scene` to be applied. This will evaluate the `scene`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved and spawned From b32474fd98c9e1305fff084978bcf82b5d8b7561 Mon Sep 17 00:00:00 2001 From: laund Date: Wed, 20 May 2026 23:55:28 +0200 Subject: [PATCH 6/8] fix mistakes (and typo, thanks CI) --- crates/bevy_scene/src/lib.rs | 8 ++++---- crates/bevy_scene/src/resolved_scene.rs | 2 +- crates/bevy_scene/src/scene.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs index b9a88e497d18e..afd12e27f9f62 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -527,7 +527,7 @@ //! # } //! # let mut world = World::new(); //! world.spawn_scene(bsn! { -//! :Player { score: 0 } +//! @Player { score: 0 } //! }); //! ``` //! @@ -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, //! } diff --git a/crates/bevy_scene/src/resolved_scene.rs b/crates/bevy_scene/src/resolved_scene.rs index 3814cf29151be..6d451fb5ef703 100644 --- a/crates/bevy_scene/src/resolved_scene.rs +++ b/crates/bevy_scene/src/resolved_scene.rs @@ -178,7 +178,7 @@ pub struct ResolvedScene { /// 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" cacheing. + /// "flattened" caching. pub entity_references: Vec, } diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index a268f8ee2eb75..c3585bd39e738 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -368,7 +368,7 @@ impl Scene for CachedSceneAsset { if let Some(handle) = context.assets.get_handle::(&self.0) && let Some(scene_patch) = context.patches.get(&handle) { - scene.use_cached(handle)?; + scene.include_cached(handle)?; context.cached = Some(scene_patch); Ok(()) } else { From b463427f3e93c8db28c92cf772bc52564555ed14 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 20 May 2026 18:05:15 -0700 Subject: [PATCH 7/8] Small error handling tweaks --- crates/bevy_scene/macros/src/bsn/parse.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/bevy_scene/macros/src/bsn/parse.rs b/crates/bevy_scene/macros/src/bsn/parse.rs index 6cae583a5ab79..bc9b82a712d2f 100644 --- a/crates/bevy_scene/macros/src/bsn/parse.rs +++ b/crates/bevy_scene/macros/src/bsn/parse.rs @@ -239,7 +239,7 @@ impl BsnScene { None }; - let err_cached = |msg: &str| { + let err_if_cached = |msg: &str| { if let Some(colon) = cached { Err(syn::Error::new(colon.span(), msg)) } else { @@ -248,7 +248,7 @@ impl BsnScene { }; if found_cached_scene { - err_cached("Cannot cache scenes more than once")?; + err_if_cached("Cannot cache scenes more than once")?; } Ok(if input.peek(LitStr) { @@ -256,18 +256,18 @@ impl BsnScene { if cached.is_none() { return Err(syn::Error::new( path.span(), - "Cannot use scene from asset path without caching, please add : prefix.", + "Cannot use scenes from asset path without caching, please add the ':' prefix.", )); } BsnScene::Asset(path) } else if input.peek(Brace) { - err_cached("Cannot cache scene expressions")?; + 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_cached("Cannot cache Scene Components with props/fields")?; + err_if_cached("Cannot cache Scene Components with props/fields")?; } BsnScene::SceneComponent(sc) } else { @@ -280,7 +280,7 @@ impl BsnScene { return Err(syn::Error::new( path.span(), format!( - "Scene component {} needs to be prefixed by @", + "Scene component {} needs to be prefixed by '@'", path_to_string(&path), ), )); @@ -292,7 +292,7 @@ impl BsnScene { args: input.parse()?, }; if func.args.0.is_some() { - err_cached("Cannot cache Scene function with arguments")?; + err_if_cached("Cannot cache Scene function with arguments")?; } BsnScene::Fn(func) } From 10b5718a23398c7cd868000090b767ba04083c7b Mon Sep 17 00:00:00 2001 From: laund Date: Thu, 21 May 2026 03:09:51 +0200 Subject: [PATCH 8/8] Update crates/bevy_scene/macros/src/lib.rs Co-authored-by: Carter Anderson --- crates/bevy_scene/macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_scene/macros/src/lib.rs b/crates/bevy_scene/macros/src/lib.rs index 5a7465f7a2d4c..515d4ed127d46 100644 --- a/crates/bevy_scene/macros/src/lib.rs +++ b/crates/bevy_scene/macros/src/lib.rs @@ -312,7 +312,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 cached 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