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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2835,6 +2835,10 @@ wasm = false
name = "bsn"
path = "examples/scene/bsn.rs"

[[example]]
name = "bsn_reconcile"
path = "examples/scene/bsn_reconcile.rs"

[[example]]
name = "ui_scene"
path = "examples/scene/ui_scene.rs"
Expand Down
25 changes: 18 additions & 7 deletions crates/bevy_ecs/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
pub use bevy_ecs_macros::GetTemplate;

use crate::{
bundle::Bundle,
bundle::{Bundle, BundleInfo},
entity::{Entity, EntityPath},
error::{BevyError, Result},
resource::Resource,
Expand Down Expand Up @@ -31,20 +31,20 @@ pub trait Template {

/// The context used to apply the current [`Template`]. This contains a reference to the entity that the template is being
/// applied to.
pub struct TemplateContext<'a> {
pub struct TemplateContext<'w, 'a> {
/// The current entity the template is being applied to
pub entity: &'a mut EntityWorldMut<'a>,
pub entity: &'a mut EntityWorldMut<'w>,
/// The scoped entities mapping for the current template context
pub scoped_entities: &'a mut ScopedEntities,
/// The entity scopes for the current template context. This matches
/// the `scoped_entities`.
pub entity_scopes: &'a EntityScopes,
}

impl<'a> TemplateContext<'a> {
impl<'w, 'a> TemplateContext<'w, 'a> {
/// Creates a new [`TemplateContext`].
pub fn new(
entity: &'a mut EntityWorldMut<'a>,
entity: &'a mut EntityWorldMut<'w>,
scoped_entities: &'a mut ScopedEntities,
entity_scopes: &'a EntityScopes,
) -> Self {
Expand Down Expand Up @@ -127,7 +127,7 @@ impl EntityScopes {
pub struct ScopedEntities(Vec<Option<Entity>>);

impl ScopedEntities {
/// Creates a new [`ScopedEntities`] with the given `size`, intialized to [`None`] (no [`Entity`] assigned).
/// Creates a new [`ScopedEntities`] with the given `size`, intialized to [`None`] (no [`Entity`] assigned).
pub fn new(size: usize) -> Self {
Self(vec![None; size])
}
Expand Down Expand Up @@ -160,7 +160,7 @@ impl ScopedEntities {
}
}

impl<'a> TemplateContext<'a> {
impl<'w, 'a> TemplateContext<'w, 'a> {
/// Retrieves a reference to the given resource `R`.
pub fn resource<R: Resource>(&self) -> &R {
self.entity.resource()
Expand Down Expand Up @@ -265,6 +265,13 @@ impl GetTemplate for Entity {
pub trait ErasedTemplate: Downcast + Send + Sync {
/// Applies this template to the given `entity`.
fn apply(&mut self, context: &mut TemplateContext) -> Result<(), BevyError>;

/// Registers the output bundle of this template with the given `world`.
///
/// Returns the [`BundleInfo`] of the registered bundle, or `None` if the template does not output a bundle.
fn register_bundle<'a>(&self, _: &'a mut World) -> Option<&'a BundleInfo> {
None
}
}

impl_downcast!(ErasedTemplate);
Expand All @@ -275,6 +282,10 @@ impl<T: Template<Output: Bundle> + Send + Sync + 'static> ErasedTemplate for T {
context.entity.insert(bundle);
Ok(())
}

fn register_bundle<'a>(&self, world: &'a mut World) -> Option<&'a BundleInfo> {
Some(world.register_bundle::<T::Output>())
}
}

// TODO: Consider cutting this
Expand Down
11 changes: 5 additions & 6 deletions crates/bevy_feathers/src/controls/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ use bevy_input_focus::tab_navigation::TabIndex;
/// system to identify which entities are buttons.
#[derive(Component, Default, Clone, Reflect, Debug, PartialEq, Eq)]
#[reflect(Component, Clone, Default)]
#[require(
Hovered,
ThemeBackgroundColor(tokens::BUTTON_BG),
ThemeFontColor(tokens::BUTTON_TEXT)
)]
pub enum ButtonVariant {
/// The standard button appearance
#[default]
Expand Down Expand Up @@ -61,11 +66,8 @@ pub fn button(props: ButtonProps) -> impl Scene {
flex_grow: 1.0,
}
Button
Hovered
EntityCursor::System(bevy_window::SystemCursorIcon::Pointer)
TabIndex(0)
ThemeBackgroundColor(tokens::BUTTON_BG)
ThemeFontColor(tokens::BUTTON_TEXT)
template_value(props.variant)
template_value(props.corners.to_border_radius(4.0))
InheritableFont {
Expand All @@ -89,11 +91,8 @@ pub fn tool_button(props: ButtonProps) -> impl Scene {
flex_grow: 1.0,
}
Button
Hovered
EntityCursor::System(bevy_window::SystemCursorIcon::Pointer)
TabIndex(0)
ThemeBackgroundColor(tokens::BUTTON_BG)
ThemeFontColor(tokens::BUTTON_TEXT)
InheritableFont {
font: fonts::REGULAR,
font_size: 14.0,
Expand Down
11 changes: 6 additions & 5 deletions crates/bevy_feathers/src/controls/checkbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,22 @@ use crate::{
/// Marker for the checkbox frame (contains both checkbox and label)
#[derive(Component, Default, Clone, Reflect)]
#[reflect(Component, Clone, Default)]
#[require(Hovered, ThemeFontColor(tokens::CHECKBOX_TEXT))]
struct CheckboxFrame;

/// Marker for the checkbox outline
#[derive(Component, Default, Clone, Reflect)]
#[reflect(Component, Clone, Default)]
#[require(
ThemeBackgroundColor(tokens::CHECKBOX_BG),
ThemeBorderColor(tokens::CHECKBOX_BORDER)
)]
struct CheckboxOutline;

/// Marker for the checkbox check mark
#[derive(Component, Default, Clone, Reflect)]
#[reflect(Component, Clone, Default)]
#[require(ThemeBorderColor(tokens::CHECKBOX_MARK))]
struct CheckboxMark;

/// Checkbox scene function.
Expand All @@ -59,10 +65,8 @@ pub fn checkbox() -> impl Scene {
}
Checkbox
CheckboxFrame
Hovered
EntityCursor::System(bevy_window::SystemCursorIcon::Pointer)
TabIndex(0)
ThemeFontColor(tokens::CHECKBOX_TEXT)
InheritableFont {
font: fonts::REGULAR,
font_size: 14.0,
Expand All @@ -75,8 +79,6 @@ pub fn checkbox() -> impl Scene {
}
CheckboxOutline
BorderRadius::all(Val::Px(4.0))
ThemeBackgroundColor(tokens::CHECKBOX_BG)
ThemeBorderColor(tokens::CHECKBOX_BORDER)
[(
// Cheesy checkmark: rotated node with L-shaped border.
Node {
Expand All @@ -92,7 +94,6 @@ pub fn checkbox() -> impl Scene {
}
UiTransform::from_rotation(Rot2::FRAC_PI_4)
CheckboxMark
ThemeBorderColor(tokens::CHECKBOX_MARK)
)]
)]
}
Expand Down
52 changes: 33 additions & 19 deletions crates/bevy_feathers/src/controls/color_slider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,38 @@ pub struct ColorSlider {
#[derive(Component, Default, Clone)]
struct ColorSliderTrack;

/// Marker for the left/right endcaps
#[derive(Component, Default, Clone)]
#[require(BackgroundColor)]
struct ColorSliderEndCap;

#[derive(Component, Default, Clone)]
#[require(
BackgroundGradient(vec![Gradient::Linear(LinearGradient {
angle: PI * 0.5,
stops: vec![
ColorStop::new(Color::NONE, Val::Percent(0.)),
ColorStop::new(Color::NONE, Val::Percent(50.)),
ColorStop::new(Color::NONE, Val::Percent(100.)),
],
color_space: InterpolationColorSpace::Srgba,
})])
)]
struct ColorSliderGradient;

/// Marker for the thumb
#[derive(Component, Default, Clone)]
#[require(
Node {
position_type: PositionType::Absolute,
left: Val::Percent(0.),
top: Val::Percent(50.),
width: Val::Px(THUMB_SIZE),
height: Val::Px(THUMB_SIZE),
border: UiRect::all(Val::Px(2.0)),
..Default::default()
}
)]
struct ColorSliderThumb;

/// Spawn a new slider widget.
Expand Down Expand Up @@ -213,36 +243,20 @@ pub fn color_slider(props: ColorSliderProps) -> impl Scene {
[
// Left endcap
(
ColorSliderEndCap
Node {
width: Val::Px({THUMB_SIZE * 0.5}),
}
template_value(RoundedCorners::Left.to_border_radius(TRACK_RADIUS))
BackgroundColor({palette::X_AXIS})
),
// Track with gradient
(
ColorSliderGradient
Node {
flex_grow: 1.0,
}
BackgroundGradient({vec![Gradient::Linear(LinearGradient {
angle: PI * 0.5,
stops: vec![
ColorStop::new(Color::NONE, Val::Percent(0.)),
ColorStop::new(Color::NONE, Val::Percent(50.)),
ColorStop::new(Color::NONE, Val::Percent(100.)),
],
color_space: InterpolationColorSpace::Srgba,
})]})
ZIndex(1)
[(
Node {
position_type: PositionType::Absolute,
left: Val::Percent(0.),
top: Val::Percent(50.),
width: Val::Px(THUMB_SIZE),
height: Val::Px(THUMB_SIZE),
border: UiRect::all(Val::Px(2.0)),
}
SliderThumb
ColorSliderThumb
BorderRadius::MAX
Expand All @@ -260,11 +274,11 @@ pub fn color_slider(props: ColorSliderProps) -> impl Scene {
),
// Right endcap
(
ColorSliderEndCap
Node {
width: Val::Px({THUMB_SIZE * 0.5}),
}
template_value(RoundedCorners::Right.to_border_radius(TRACK_RADIUS))
BackgroundColor({palette::Z_AXIS})
),
]
)
Expand Down
13 changes: 9 additions & 4 deletions crates/bevy_feathers/src/controls/radio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,21 @@ use bevy_scene2::prelude::*;
/// Marker for the radio outline
#[derive(Component, Default, Clone, Reflect)]
#[reflect(Component, Clone, Default)]
#[require(ThemeBorderColor(tokens::RADIO_BORDER))]
struct RadioOutline;

/// Marker for the radio check mark
#[derive(Component, Default, Clone, Reflect)]
#[reflect(Component, Clone, Default)]
#[require(ThemeBackgroundColor(tokens::RADIO_MARK))]
struct RadioMark;

/// Marker for the radio frame
#[derive(Component, Default, Clone, Reflect)]
#[reflect(Component, Clone, Default)]
#[require(Hovered, ThemeFontColor(tokens::RADIO_TEXT))]
pub struct RadioFrame;

/// Radio scene function.
pub fn radio() -> impl Scene {
bsn! {
Expand All @@ -49,10 +57,9 @@ pub fn radio() -> impl Scene {
column_gap: Val::Px(4.0),
}
RadioButton
Hovered
RadioFrame
EntityCursor::System(bevy_window::SystemCursorIcon::Pointer)
TabIndex(0)
ThemeFontColor(tokens::RADIO_TEXT)
InheritableFont {
font: fonts::REGULAR,
font_size: 14.0,
Expand All @@ -68,7 +75,6 @@ pub fn radio() -> impl Scene {
}
RadioOutline
BorderRadius::MAX
ThemeBorderColor(tokens::RADIO_BORDER)
[(
// Cheesy checkmark: rotated node with L-shaped border.
Node {
Expand All @@ -77,7 +83,6 @@ pub fn radio() -> impl Scene {
}
BorderRadius::MAX
RadioMark
ThemeBackgroundColor(tokens::RADIO_MARK)
)]
)]
}
Expand Down
37 changes: 16 additions & 21 deletions crates/bevy_feathers/src/controls/slider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ use crate::{

/// Slider template properties, passed to [`slider`] function.
pub struct SliderProps {
/// Slider current value
pub value: f32,
/// Slider minimum value
pub min: f32,
/// Slider maximum value
Expand All @@ -44,22 +42,31 @@ pub struct SliderProps {

impl Default for SliderProps {
fn default() -> Self {
Self {
value: 0.0,
min: 0.0,
max: 1.0,
}
Self { min: 0.0, max: 1.0 }
}
}

#[derive(Component, Default, Clone, Reflect)]
#[require(Slider)]
#[reflect(Component, Clone, Default)]
#[require(
SliderValue,
BackgroundGradient(vec![Gradient::Linear(LinearGradient {
angle: PI * 0.5,
stops: vec![
ColorStop::new(Color::NONE, Val::Percent(0.)),
ColorStop::new(Color::NONE, Val::Percent(50.)),
ColorStop::new(Color::NONE, Val::Percent(50.)),
ColorStop::new(Color::NONE, Val::Percent(100.)),
],
color_space: InterpolationColorSpace::Srgba,
})])
)]
struct SliderStyle;

/// Marker for the text
#[derive(Component, Default, Clone, Reflect)]
#[reflect(Component, Clone, Default)]
#[require(Text)]
struct SliderValueText;

/// Slider scene function.
Expand All @@ -80,22 +87,10 @@ pub fn slider(props: SliderProps) -> impl Scene {
track_click: TrackClick::Drag,
}
SliderStyle
SliderValue({props.value})
SliderRange::new(props.min, props.max)
EntityCursor::System(bevy_window::SystemCursorIcon::EwResize)
TabIndex(0)
template_value(RoundedCorners::All.to_border_radius(6.0))
// Use a gradient to draw the moving bar
BackgroundGradient({vec![Gradient::Linear(LinearGradient {
angle: PI * 0.5,
stops: vec![
ColorStop::new(Color::NONE, Val::Percent(0.)),
ColorStop::new(Color::NONE, Val::Percent(50.)),
ColorStop::new(Color::NONE, Val::Percent(50.)),
ColorStop::new(Color::NONE, Val::Percent(100.)),
],
color_space: InterpolationColorSpace::Srgba,
})]})
[(
// Text container
Node {
Expand All @@ -110,7 +105,7 @@ pub fn slider(props: SliderProps) -> impl Scene {
font: fonts::MONO,
font_size: 12.0,
}
[(Text::new("10.0") ThemedText SliderValueText)]
[(ThemedText SliderValueText)]
)]
}
}
Expand Down
Loading