Skip to content

Commit

Permalink
Merge Style properties into Node. Use ComputedNode for computed prope…
Browse files Browse the repository at this point in the history
…rties. (bevyengine#15975)

# Objective

Continue improving the user experience of our UI Node API in the
direction specified by [Bevy's Next Generation Scene / UI
System](bevyengine#14437)

## Solution

As specified in the document above, merge `Style` fields into `Node`,
and move "computed Node fields" into `ComputedNode` (I chose this name
over something like `ComputedNodeLayout` because it currently contains
more than just layout info. If we want to break this up / rename these
concepts, lets do that in a separate PR). `Style` has been removed.

This accomplishes a number of goals:

## Ergonomics wins

Specifying both `Node` and `Style` is now no longer required for
non-default styles

Before:
```rust
commands.spawn((
    Node::default(),
    Style {
        width:  Val::Px(100.),
        ..default()
    },
));
```

After:

```rust
commands.spawn(Node {
    width:  Val::Px(100.),
    ..default()
});
```

## Conceptual clarity

`Style` was never a comprehensive "style sheet". It only defined "core"
style properties that all `Nodes` shared. Any "styled property" that
couldn't fit that mold had to be in a separate component. A "real" style
system would style properties _across_ components (`Node`, `Button`,
etc). We have plans to build a true style system (see the doc linked
above).

By moving the `Style` fields to `Node`, we fully embrace `Node` as the
driving concept and remove the "style system" confusion.

## Next Steps

* Consider identifying and splitting out "style properties that aren't
core to Node". This should not happen for Bevy 0.15.

---

## Migration Guide

Move any fields set on `Style` into `Node` and replace all `Style`
component usage with `Node`.

Before:
```rust
commands.spawn((
    Node::default(),
    Style {
        width:  Val::Px(100.),
        ..default()
    },
));
```

After:

```rust
commands.spawn(Node {
    width:  Val::Px(100.),
    ..default()
});
```

For any usage of the "computed node properties" that used to live on
`Node`, use `ComputedNode` instead:

Before:
```rust
fn system(nodes: Query<&Node>) {
    for node in &nodes {
        let computed_size = node.size();
    }
}
```

After:
```rust
fn system(computed_nodes: Query<&ComputedNode>) {
    for computed_node in &computed_nodes {
        let computed_size = computed_node.size();
    }
}
```
  • Loading branch information
cart authored Oct 18, 2024
1 parent 624f573 commit 015f2c6
Show file tree
Hide file tree
Showing 149 changed files with 1,257 additions and 1,615 deletions.
6 changes: 2 additions & 4 deletions crates/bevy_dev_tools/src/fps_overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ use bevy_ecs::{
use bevy_hierarchy::{BuildChildren, ChildBuild};
use bevy_render::view::Visibility;
use bevy_text::{Font, TextColor, TextFont, TextSpan};
use bevy_ui::Node;
use bevy_ui::{
widget::{Text, TextUiWriter},
GlobalZIndex, PositionType, Style,
GlobalZIndex, Node, PositionType,
};
use bevy_utils::default;

Expand Down Expand Up @@ -89,8 +88,7 @@ struct FpsText;
fn setup(mut commands: Commands, overlay_config: Res<FpsOverlayConfig>) {
commands
.spawn((
Node::default(),
Style {
Node {
// We need to make sure the overlay doesn't affect the position of other UI nodes
position_type: PositionType::Absolute,
..default()
Expand Down
14 changes: 7 additions & 7 deletions crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use bevy_render::{
view::{RenderLayers, VisibilitySystems},
};
use bevy_transform::{prelude::GlobalTransform, TransformSystem};
use bevy_ui::{DefaultUiCamera, Display, Node, Style, TargetCamera, UiScale};
use bevy_ui::{ComputedNode, DefaultUiCamera, Display, Node, TargetCamera, UiScale};
use bevy_utils::{default, warn_once};
use bevy_window::{PrimaryWindow, Window, WindowRef};

Expand All @@ -37,7 +37,7 @@ struct LayoutRect {
}

impl LayoutRect {
fn new(trans: &GlobalTransform, node: &Node, scale: f32) -> Self {
fn new(trans: &GlobalTransform, node: &ComputedNode, scale: f32) -> Self {
let mut this = Self {
pos: trans.translation().xy() * scale,
size: node.size() * scale,
Expand Down Expand Up @@ -123,8 +123,8 @@ fn outline_nodes(outline: &OutlineParam, draw: &mut InsetGizmo, this_entity: Ent
return;
};

for (entity, trans, node, style, children) in outline.nodes.iter_many(to_iter) {
if style.is_none() || style.is_some_and(|s| matches!(s.display, Display::None)) {
for (entity, trans, node, computed_node, children) in outline.nodes.iter_many(to_iter) {
if matches!(node.display, Display::None) {
continue;
}

Expand All @@ -133,7 +133,7 @@ fn outline_nodes(outline: &OutlineParam, draw: &mut InsetGizmo, this_entity: Ent
continue;
}
}
let rect = LayoutRect::new(trans, node, scale);
let rect = LayoutRect::new(trans, computed_node, scale);
outline_node(entity, rect, draw);
if children.is_some() {
outline_nodes(outline, draw, entity, scale);
Expand All @@ -146,7 +146,7 @@ type NodesQuery = (
Entity,
&'static GlobalTransform,
&'static Node,
Option<&'static Style>,
&'static ComputedNode,
Option<&'static Children>,
);

Expand Down Expand Up @@ -178,7 +178,7 @@ fn outline_roots(
(
Entity,
&GlobalTransform,
&Node,
&ComputedNode,
Option<&ViewVisibility>,
Option<&TargetCamera>,
),
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/query/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,9 +352,9 @@ unsafe impl<T: Component> QueryFilter for Without<T> {
/// # #[derive(Component, Debug)]
/// # struct Color {};
/// # #[derive(Component)]
/// # struct Style {};
/// # struct Node {};
/// #
/// fn print_cool_entity_system(query: Query<Entity, Or<(Changed<Color>, Changed<Style>)>>) {
/// fn print_cool_entity_system(query: Query<Entity, Or<(Changed<Color>, Changed<Node>)>>) {
/// for entity in &query {
/// println!("Entity {:?} got a new style or color", entity);
/// }
Expand Down
8 changes: 4 additions & 4 deletions crates/bevy_text/src/text_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,11 @@ impl<'a, R: TextRoot> Iterator for TextSpanIter<'a, R> {
fn next(&mut self) -> Option<Self::Item> {
// Root
if let Some(root_entity) = self.root_entity.take() {
if let Ok((text, style, color, maybe_children)) = self.roots.get(root_entity) {
if let Ok((text, text_font, color, maybe_children)) = self.roots.get(root_entity) {
if let Some(children) = maybe_children {
self.stack.push((children, 0));
}
return Some((root_entity, 0, text.read_span(), style, color.0));
return Some((root_entity, 0, text.read_span(), text_font, color.0));
}
return None;
}
Expand All @@ -193,15 +193,15 @@ impl<'a, R: TextRoot> Iterator for TextSpanIter<'a, R> {
*idx += 1;

let entity = *child;
let Ok((span, style, color, maybe_children)) = self.spans.get(entity) else {
let Ok((span, text_font, color, maybe_children)) = self.spans.get(entity) else {
continue;
};

let depth = self.stack.len();
if let Some(children) = maybe_children {
self.stack.push((children, 0));
}
return Some((entity, depth, span.read_span(), style, color.0));
return Some((entity, depth, span.read_span(), text_font, color.0));
}

// All children at this stack entry have been iterated.
Expand Down
8 changes: 6 additions & 2 deletions crates/bevy_ui/src/accessibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
experimental::UiChildren,
prelude::{Button, Label},
widget::TextUiReader,
Node, UiImage,
ComputedNode, UiImage,
};
use bevy_a11y::{
accesskit::{NodeBuilder, Rect, Role},
Expand Down Expand Up @@ -38,7 +38,11 @@ fn calc_name(

fn calc_bounds(
camera: Query<(&Camera, &GlobalTransform)>,
mut nodes: Query<(&mut AccessibilityNode, Ref<Node>, Ref<GlobalTransform>)>,
mut nodes: Query<(
&mut AccessibilityNode,
Ref<ComputedNode>,
Ref<GlobalTransform>,
)>,
) {
if let Ok((camera, camera_transform)) = camera.get_single() {
for (mut accessible, node, transform) in &mut nodes {
Expand Down
7 changes: 4 additions & 3 deletions crates/bevy_ui/src/focus.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
CalculatedClip, DefaultUiCamera, Node, ResolvedBorderRadius, TargetCamera, UiScale, UiStack,
CalculatedClip, ComputedNode, DefaultUiCamera, ResolvedBorderRadius, TargetCamera, UiScale,
UiStack,
};
use bevy_ecs::{
change_detection::DetectChangesMut,
Expand Down Expand Up @@ -74,7 +75,7 @@ impl Default for Interaction {
///
/// It can be used alongside [`Interaction`] to get the position of the press.
///
/// The component is updated when it is in the same entity with [`Node`].
/// The component is updated when it is in the same entity with [`Node`](crate::Node).
#[derive(Component, Copy, Clone, Default, PartialEq, Debug, Reflect)]
#[reflect(Component, Default, PartialEq, Debug)]
#[cfg_attr(
Expand Down Expand Up @@ -135,7 +136,7 @@ pub struct State {
#[query_data(mutable)]
pub struct NodeQuery {
entity: Entity,
node: &'static Node,
node: &'static ComputedNode,
global_transform: &'static GlobalTransform,
interaction: Option<&'static mut Interaction>,
relative_cursor_position: Option<&'static mut RelativeCursorPosition>,
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ui/src/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize};

/// Represents the possible value types for layout properties.
///
/// This enum allows specifying values for various [`Style`](crate::Style) properties in different units,
/// This enum allows specifying values for various [`Node`](crate::Node) properties in different units,
/// such as logical pixels, percentages, or automatically determined values.
#[derive(Copy, Clone, Debug, Reflect)]
#[reflect(Default, PartialEq, Debug)]
Expand All @@ -18,7 +18,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
reflect(Serialize, Deserialize)
)]
pub enum Val {
/// Automatically determine the value based on the context and other [`Style`](crate::Style) properties.
/// Automatically determine the value based on the context and other [`Node`](crate::Node) properties.
Auto,
/// Set this value in logical pixels.
Px(f32),
Expand All @@ -27,7 +27,7 @@ pub enum Val {
/// If the UI node has no parent, the percentage is calculated based on the window's length
/// along the corresponding axis.
///
/// The chosen axis depends on the [`Style`](crate::Style) field set:
/// The chosen axis depends on the [`Node`](crate::Node) field set:
/// * For `flex_basis`, the percentage is relative to the main-axis length determined by the `flex_direction`.
/// * For `gap`, `min_size`, `size`, and `max_size`:
/// - `width` is relative to the parent's width.
Expand Down
91 changes: 43 additions & 48 deletions crates/bevy_ui/src/layout/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use taffy::style_helpers;
use crate::{
AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, GridAutoFlow,
GridPlacement, GridTrack, GridTrackRepetition, JustifyContent, JustifyItems, JustifySelf,
MaxTrackSizingFunction, MinTrackSizingFunction, OverflowAxis, PositionType, RepeatedGridTrack,
Style, UiRect, Val,
MaxTrackSizingFunction, MinTrackSizingFunction, Node, OverflowAxis, PositionType,
RepeatedGridTrack, UiRect, Val,
};

use super::LayoutContext;
Expand Down Expand Up @@ -63,91 +63,86 @@ impl UiRect {
}
}

pub fn from_style(
context: &LayoutContext,
style: &Style,
ignore_border: bool,
) -> taffy::style::Style {
pub fn from_node(node: &Node, context: &LayoutContext, ignore_border: bool) -> taffy::style::Style {
taffy::style::Style {
display: style.display.into(),
display: node.display.into(),
overflow: taffy::Point {
x: style.overflow.x.into(),
y: style.overflow.y.into(),
x: node.overflow.x.into(),
y: node.overflow.y.into(),
},
scrollbar_width: 0.0,
position: style.position_type.into(),
flex_direction: style.flex_direction.into(),
flex_wrap: style.flex_wrap.into(),
align_items: style.align_items.into(),
justify_items: style.justify_items.into(),
align_self: style.align_self.into(),
justify_self: style.justify_self.into(),
align_content: style.align_content.into(),
justify_content: style.justify_content.into(),
position: node.position_type.into(),
flex_direction: node.flex_direction.into(),
flex_wrap: node.flex_wrap.into(),
align_items: node.align_items.into(),
justify_items: node.justify_items.into(),
align_self: node.align_self.into(),
justify_self: node.justify_self.into(),
align_content: node.align_content.into(),
justify_content: node.justify_content.into(),
inset: taffy::Rect {
left: style.left.into_length_percentage_auto(context),
right: style.right.into_length_percentage_auto(context),
top: style.top.into_length_percentage_auto(context),
bottom: style.bottom.into_length_percentage_auto(context),
left: node.left.into_length_percentage_auto(context),
right: node.right.into_length_percentage_auto(context),
top: node.top.into_length_percentage_auto(context),
bottom: node.bottom.into_length_percentage_auto(context),
},
margin: style
margin: node
.margin
.map_to_taffy_rect(|m| m.into_length_percentage_auto(context)),
padding: style
padding: node
.padding
.map_to_taffy_rect(|m| m.into_length_percentage(context)),
// Ignore border for leaf nodes as it isn't implemented in the rendering engine.
// TODO: Implement rendering of border for leaf nodes
border: if ignore_border {
taffy::Rect::zero()
} else {
style
.border
node.border
.map_to_taffy_rect(|m| m.into_length_percentage(context))
},
flex_grow: style.flex_grow,
flex_shrink: style.flex_shrink,
flex_basis: style.flex_basis.into_dimension(context),
flex_grow: node.flex_grow,
flex_shrink: node.flex_shrink,
flex_basis: node.flex_basis.into_dimension(context),
size: taffy::Size {
width: style.width.into_dimension(context),
height: style.height.into_dimension(context),
width: node.width.into_dimension(context),
height: node.height.into_dimension(context),
},
min_size: taffy::Size {
width: style.min_width.into_dimension(context),
height: style.min_height.into_dimension(context),
width: node.min_width.into_dimension(context),
height: node.min_height.into_dimension(context),
},
max_size: taffy::Size {
width: style.max_width.into_dimension(context),
height: style.max_height.into_dimension(context),
width: node.max_width.into_dimension(context),
height: node.max_height.into_dimension(context),
},
aspect_ratio: style.aspect_ratio,
aspect_ratio: node.aspect_ratio,
gap: taffy::Size {
width: style.column_gap.into_length_percentage(context),
height: style.row_gap.into_length_percentage(context),
width: node.column_gap.into_length_percentage(context),
height: node.row_gap.into_length_percentage(context),
},
grid_auto_flow: style.grid_auto_flow.into(),
grid_template_rows: style
grid_auto_flow: node.grid_auto_flow.into(),
grid_template_rows: node
.grid_template_rows
.iter()
.map(|track| track.clone_into_repeated_taffy_track(context))
.collect::<Vec<_>>(),
grid_template_columns: style
grid_template_columns: node
.grid_template_columns
.iter()
.map(|track| track.clone_into_repeated_taffy_track(context))
.collect::<Vec<_>>(),
grid_auto_rows: style
grid_auto_rows: node
.grid_auto_rows
.iter()
.map(|track| track.into_taffy_track(context))
.collect::<Vec<_>>(),
grid_auto_columns: style
grid_auto_columns: node
.grid_auto_columns
.iter()
.map(|track| track.into_taffy_track(context))
.collect::<Vec<_>>(),
grid_row: style.grid_row.into(),
grid_column: style.grid_column.into(),
grid_row: node.grid_row.into(),
grid_column: node.grid_column.into(),
}
}

Expand Down Expand Up @@ -448,7 +443,7 @@ mod tests {
use sh::TaffyZero;
use taffy::style_helpers as sh;

let bevy_style = Style {
let node = Node {
display: Display::Flex,
position_type: PositionType::Absolute,
left: Val::ZERO,
Expand Down Expand Up @@ -516,7 +511,7 @@ mod tests {
grid_row: GridPlacement::span(3),
};
let viewport_values = LayoutContext::new(1.0, bevy_math::Vec2::new(800., 600.));
let taffy_style = from_style(&viewport_values, &bevy_style, false);
let taffy_style = from_node(&node, &viewport_values, false);
assert_eq!(taffy_style.display, taffy::style::Display::Flex);
assert_eq!(taffy_style.position, taffy::style::Position::Absolute);
assert_eq!(
Expand Down
Loading

0 comments on commit 015f2c6

Please sign in to comment.