use nalgebra_glm::{Vec2, Vec4};
use crate::ecs::text::components::{TextAlignment, VerticalAlignment};
use crate::ecs::tween::easing::EasingFunction;
use crate::ecs::ui::components::{
AutoSizeMode, DragPayload, StateTransition, ThemeColor, UiAnimationPhase, UiAnimationType,
UiDepthMode, UiDragSource, UiDropTarget, UiLayoutNode, UiLayoutRoot, UiNodeAnimation,
UiNodeColor, UiNodeContent, UiNodeInteraction, UiStateWeights, UiThemeBinding,
};
use crate::ecs::ui::layout_types::{
BoundaryLayout, FlowAlignment, FlowDirection, FlowLayout, GridLayout, ScalingMode, SolidLayout,
UiLayoutType, WindowLayout,
};
use crate::ecs::ui::state::{UiBase, UiHover, UiStateTrait};
use crate::ecs::ui::theme::UiTheme;
use crate::ecs::ui::types::Anchor;
use crate::ecs::ui::units::UiValue;
use crate::ecs::world::World;
use crate::render::wgpu::passes::geometry::UiLayer;
pub struct UiTreeBuilder<'a> {
world: &'a mut World,
root: freecs::Entity,
current_parent: freecs::Entity,
parent_stack: Vec<freecs::Entity>,
next_tab_index: i32,
}
impl<'a> UiTreeBuilder<'a> {
pub fn new(world: &'a mut World) -> Self {
let root = world.spawn();
world.core.add_components(root, crate::ecs::world::PARENT);
world
.ui
.add_components(root, crate::ecs::world::UI_LAYOUT_ROOT);
if let Some(root_comp) = world.ui.get_ui_layout_root_mut(root) {
*root_comp = UiLayoutRoot::default();
}
world.resources.children_cache_valid = false;
world.resources.retained_ui.layout_dirty = true;
Self {
world,
root,
current_parent: root,
parent_stack: Vec::new(),
next_tab_index: 0,
}
}
pub fn new_for_window(world: &'a mut World, window_index: usize) -> Self {
let root = world.spawn();
world.core.add_components(root, crate::ecs::world::PARENT);
world
.ui
.add_components(root, crate::ecs::world::UI_LAYOUT_ROOT);
if let Some(root_comp) = world.ui.get_ui_layout_root_mut(root) {
*root_comp = UiLayoutRoot {
target_window: Some(window_index),
..UiLayoutRoot::default()
};
}
world.resources.children_cache_valid = false;
world.resources.retained_ui.layout_dirty = true;
Self {
world,
root,
current_parent: root,
parent_stack: Vec::new(),
next_tab_index: 0,
}
}
pub fn from_parent(world: &'a mut World, parent_entity: freecs::Entity) -> Self {
world.resources.children_cache_valid = false;
world.resources.retained_ui.layout_dirty = true;
Self {
world,
root: parent_entity,
current_parent: parent_entity,
parent_stack: Vec::new(),
next_tab_index: 0,
}
}
pub fn finish_subtree(self) {}
pub fn assign_tab_index(&mut self, entity: freecs::Entity) {
if let Some(interaction) = self.world.ui.get_ui_node_interaction_mut(entity) {
interaction.tab_index = Some(self.next_tab_index);
}
self.next_tab_index += 1;
}
pub fn with_absolute_scale(self, scale: f32) -> Self {
if let Some(root) = self.world.ui.get_ui_layout_root_mut(self.root) {
root.absolute_scale = scale;
}
self
}
pub fn with_default_font_size(self, size: f32) -> Self {
if let Some(root) = self.world.ui.get_ui_layout_root_mut(self.root) {
root.default_font_size = size;
}
self
}
pub fn root_entity(&self) -> freecs::Entity {
self.root
}
pub fn current_parent(&self) -> freecs::Entity {
self.current_parent
}
pub fn world_mut(&mut self) -> &mut World {
self.world
}
pub fn active_theme(&self) -> &UiTheme {
self.world.resources.retained_ui.theme_state.active_theme()
}
pub fn add_node(&mut self) -> UiNodeBuilder<'_, 'a> {
let entity = self.world.spawn();
self.world
.core
.add_components(entity, crate::ecs::world::PARENT);
self.world.ui.add_components(
entity,
crate::ecs::world::UI_LAYOUT_NODE
| crate::ecs::world::UI_NODE_COLOR
| crate::ecs::world::UI_NODE_CONTENT
| crate::ecs::world::UI_STATE_WEIGHTS,
);
if let Some(parent) = self.world.core.get_parent_mut(entity) {
*parent = crate::ecs::transform::components::Parent(Some(self.current_parent));
}
if let Some(node) = self.world.ui.get_ui_layout_node_mut(entity) {
*node = UiLayoutNode::default();
}
if let Some(color) = self.world.ui.get_ui_node_color_mut(entity) {
*color = UiNodeColor::default();
}
if let Some(content) = self.world.ui.get_ui_node_content_mut(entity) {
*content = UiNodeContent::default();
}
if let Some(weights) = self.world.ui.get_ui_state_weights_mut(entity) {
*weights = UiStateWeights::default();
}
self.world.resources.children_cache_valid = false;
self.world.resources.retained_ui.layout_dirty = true;
UiNodeBuilder { tree: self, entity }
}
pub fn push_parent(&mut self, entity: freecs::Entity) {
debug_assert!(
entity != self.current_parent && !self.parent_stack.contains(&entity),
"push_parent cycle detected: entity {:?} is already in the parent chain",
entity
);
self.parent_stack.push(self.current_parent);
self.current_parent = entity;
}
pub fn pop_parent(&mut self) {
if let Some(parent) = self.parent_stack.pop() {
self.current_parent = parent;
}
}
pub fn within_content(&mut self, entity: freecs::Entity, f: impl FnOnce(&mut UiTreeBuilder)) {
let content = self.world.ui_widget_content(entity).unwrap_or(entity);
self.push_parent(content);
f(self);
self.pop_parent();
}
pub fn finish(self) -> freecs::Entity {
#[cfg(debug_assertions)]
self.validate_layout();
self.root
}
#[cfg(debug_assertions)]
fn validate_layout(&self) {
let children: Vec<freecs::Entity> = self
.world
.resources
.children_cache
.get(&self.root)
.map(|v| v.to_vec())
.unwrap_or_default();
let root_has_flow = self
.world
.ui
.get_ui_layout_node(self.root)
.is_some_and(|n| n.flow_layout.is_some());
for child in &children {
if let Some(node) = self.world.ui.get_ui_layout_node(*child)
&& !root_has_flow
&& let Some(ref child_size) = node.flow_child_size
{
let has_relative = child_size.relative.is_some()
|| child_size.relative_width.is_some()
|| child_size.relative_height.is_some();
if has_relative {
eprintln!(
"UI layout warning: entity {:?} has flow_child_size with relative units but parent {:?} has no flow layout",
child, self.root
);
}
}
}
}
}
pub struct UiNodeBuilder<'b, 'a> {
tree: &'b mut UiTreeBuilder<'a>,
entity: freecs::Entity,
}
impl<'b, 'a> UiNodeBuilder<'b, 'a> {
pub fn entity(&self) -> freecs::Entity {
self.entity
}
pub fn boundary(
self,
position_1: impl Into<UiValue<Vec2>>,
position_2: impl Into<UiValue<Vec2>>,
) -> Self {
let layout = UiLayoutType::Boundary(BoundaryLayout {
position_1: position_1.into(),
position_2: position_2.into(),
});
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.layouts[UiBase::INDEX] = Some(layout);
}
self
}
pub fn window(
self,
position: impl Into<UiValue<Vec2>>,
size: impl Into<UiValue<Vec2>>,
anchor: Anchor,
) -> Self {
let layout = UiLayoutType::Window(WindowLayout {
position: position.into(),
size: size.into(),
anchor,
});
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.layouts[UiBase::INDEX] = Some(layout);
}
self
}
pub fn solid(
self,
size: impl Into<UiValue<Vec2>>,
scaling: ScalingMode,
alignment: Vec2,
) -> Self {
let layout = UiLayoutType::Solid(SolidLayout {
size: size.into(),
scaling,
alignment,
});
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.layouts[UiBase::INDEX] = Some(layout);
}
self
}
pub fn flow(self, direction: FlowDirection, padding: f32, spacing: f32) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.flow_layout = Some(FlowLayout {
direction,
padding,
spacing,
alignment: FlowAlignment::Start,
cross_alignment: FlowAlignment::Start,
wrap: false,
});
}
self
}
pub fn flow_with_alignment(
self,
direction: FlowDirection,
padding: f32,
spacing: f32,
alignment: FlowAlignment,
cross_alignment: FlowAlignment,
) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.flow_layout = Some(FlowLayout {
direction,
padding,
spacing,
alignment,
cross_alignment,
wrap: false,
});
}
self
}
pub fn flow_wrap(self) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity)
&& let Some(flow) = &mut node.flow_layout
{
flow.wrap = true;
}
self
}
pub fn grid(
self,
columns: usize,
row_height: f32,
padding: f32,
column_spacing: f32,
row_spacing: f32,
) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.grid_layout = Some(GridLayout {
columns,
row_height,
padding,
column_spacing,
row_spacing,
min_column_width: None,
});
}
self
}
pub fn auto_grid(self, min_column_width: f32, row_height: f32, spacing: f32) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.grid_layout = Some(GridLayout {
columns: 1,
row_height,
padding: spacing,
column_spacing: spacing,
row_spacing: spacing,
min_column_width: Some(min_column_width),
});
}
self
}
pub fn flow_child(self, size: impl Into<UiValue<Vec2>>) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.flow_child_size = Some(size.into());
}
self
}
pub fn flex_grow(self, weight: f32) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.flex_grow = Some(weight);
}
self
}
pub fn flex_shrink(self, factor: f32) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.flex_shrink = Some(factor);
}
self
}
pub fn with_min_size(self, min: Vec2) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.min_size = Some(min);
}
self
}
pub fn with_max_size(self, max: Vec2) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.max_size = Some(max);
}
self
}
pub fn with_z_index(self, z: i32) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.z_index = Some(z);
}
self
}
pub fn auto_size(self, mode: AutoSizeMode) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.auto_size = mode;
}
self
}
pub fn auto_size_padding(self, padding: Vec2) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.auto_size_padding = padding;
}
self
}
pub fn with_state_layout<S: UiStateTrait>(self, layout: UiLayoutType) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.layouts[S::INDEX] = Some(layout);
}
self
}
pub fn with_hover_layout(self, layout: UiLayoutType) -> Self {
self.with_state_layout::<UiHover>(layout)
}
pub fn with_rect(self, corner_radius: f32, border_width: f32, border_color: Vec4) -> Self {
if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity) {
*content = UiNodeContent::Rect {
corner_radius,
border_width,
border_color,
};
}
self
}
pub fn with_text(self, text: &str, font_size: f32) -> Self {
let text_slot = self.tree.world.resources.text_cache.add_text(text);
if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity) {
*content = UiNodeContent::Text {
text_slot,
font_index: 0,
font_size_override: Some(font_size),
outline_color: Vec4::new(0.0, 0.0, 0.0, 0.0),
outline_width: 0.0,
alignment: TextAlignment::Center,
vertical_alignment: VerticalAlignment::Middle,
overflow: crate::ecs::ui::components::TextOverflow::default(),
monospace_width: None,
};
}
self
}
pub fn with_text_slot(self, text_slot: usize, font_size: f32) -> Self {
if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity) {
*content = UiNodeContent::Text {
text_slot,
font_index: 0,
font_size_override: Some(font_size),
outline_color: Vec4::new(0.0, 0.0, 0.0, 0.0),
outline_width: 0.0,
alignment: TextAlignment::Center,
vertical_alignment: VerticalAlignment::Middle,
overflow: crate::ecs::ui::components::TextOverflow::default(),
monospace_width: None,
};
}
self
}
pub fn with_monospace_width(self, width: f32) -> Self {
if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity)
&& let UiNodeContent::Text {
monospace_width, ..
} = content
{
*monospace_width = Some(width);
}
self
}
pub fn with_font_index(self, index: usize) -> Self {
if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity)
&& let UiNodeContent::Text { font_index, .. } = content
{
*font_index = index;
}
self
}
pub fn with_text_alignment(
self,
text_alignment: TextAlignment,
text_vertical_alignment: VerticalAlignment,
) -> Self {
if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity)
&& let UiNodeContent::Text {
alignment,
vertical_alignment,
..
} = content
{
*alignment = text_alignment;
*vertical_alignment = text_vertical_alignment;
}
self
}
pub fn with_text_outline(self, color: Vec4, width: f32) -> Self {
if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity)
&& let UiNodeContent::Text {
outline_color,
outline_width,
..
} = content
{
*outline_color = color;
*outline_width = width;
}
self
}
pub fn with_text_overflow(self, mode: crate::ecs::ui::components::TextOverflow) -> Self {
if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity)
&& let UiNodeContent::Text { overflow, .. } = content
{
*overflow = mode;
}
self
}
pub fn with_text_wrap(self) -> Self {
self.with_text_overflow(crate::ecs::ui::components::TextOverflow::Wrap)
}
pub fn with_image(self, texture_index: u32) -> Self {
if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity) {
*content = UiNodeContent::Image {
texture_index,
uv_min: Vec2::new(0.0, 0.0),
uv_max: Vec2::new(1.0, 1.0),
};
}
self
}
pub fn with_image_uv(self, texture_index: u32, uv_min: Vec2, uv_max: Vec2) -> Self {
if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity) {
*content = UiNodeContent::Image {
texture_index,
uv_min,
uv_max,
};
}
self
}
pub fn with_color<S: UiStateTrait>(self, color: Vec4) -> Self {
if let Some(node_color) = self.tree.world.ui.get_ui_node_color_mut(self.entity) {
node_color.colors[S::INDEX] = Some(color);
}
if let Some(binding) = self.tree.world.ui.get_ui_theme_binding_mut(self.entity) {
binding.color_roles[S::INDEX] = None;
}
self
}
pub fn with_theme_color<S: UiStateTrait>(self, role: ThemeColor) -> Self {
let color = {
let theme = self
.tree
.world
.resources
.retained_ui
.theme_state
.active_theme();
role.resolve(theme)
};
if let Some(node_color) = self.tree.world.ui.get_ui_node_color_mut(self.entity) {
node_color.colors[S::INDEX] = Some(color);
}
if self
.tree
.world
.ui
.get_ui_theme_binding(self.entity)
.is_none()
{
self.tree
.world
.ui
.set_ui_theme_binding(self.entity, UiThemeBinding::default());
}
if let Some(binding) = self.tree.world.ui.get_ui_theme_binding_mut(self.entity) {
binding.color_roles[S::INDEX] = Some(role);
}
self
}
pub fn with_theme_border_color(self, role: ThemeColor) -> Self {
let color = {
let theme = self
.tree
.world
.resources
.retained_ui
.theme_state
.active_theme();
role.resolve(theme)
};
if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity)
&& let UiNodeContent::Rect { border_color, .. } = content
{
*border_color = color;
}
if self
.tree
.world
.ui
.get_ui_theme_binding(self.entity)
.is_none()
{
self.tree
.world
.ui
.set_ui_theme_binding(self.entity, UiThemeBinding::default());
}
if let Some(binding) = self.tree.world.ui.get_ui_theme_binding_mut(self.entity) {
binding.border_color_role = Some(role);
}
self
}
pub fn with_interaction(self) -> Self {
self.tree
.world
.ui
.set_ui_node_interaction(self.entity, UiNodeInteraction::default());
self
}
pub fn with_test_id(self, id: &str) -> Self {
let this = if self
.tree
.world
.ui
.get_ui_node_interaction(self.entity)
.is_none()
{
self.with_interaction()
} else {
self
};
if let Some(interaction) = this.tree.world.ui.get_ui_node_interaction_mut(this.entity) {
interaction.test_id = Some(id.to_string());
}
this.tree
.world
.resources
.retained_ui
.test_id_map
.insert(id.to_string(), this.entity);
this
}
pub fn with_cursor_icon(self, icon: winit::window::CursorIcon) -> Self {
if let Some(interaction) = self.tree.world.ui.get_ui_node_interaction_mut(self.entity) {
interaction.cursor_icon = Some(icon);
}
self
}
pub fn with_tooltip(self, text: &str) -> Self {
let this = if self
.tree
.world
.ui
.get_ui_node_interaction(self.entity)
.is_none()
{
self.with_interaction()
} else {
self
};
if let Some(interaction) = this.tree.world.ui.get_ui_node_interaction_mut(this.entity) {
interaction.tooltip_text = Some(text.to_string());
}
this
}
pub fn with_tooltip_entity(self, tooltip_entity: freecs::Entity) -> Self {
let this = if self
.tree
.world
.ui
.get_ui_node_interaction(self.entity)
.is_none()
{
self.with_interaction()
} else {
self
};
if let Some(interaction) = this.tree.world.ui.get_ui_node_interaction_mut(this.entity) {
interaction.tooltip_entity = Some(tooltip_entity);
}
this
}
pub fn with_tab_index(self, index: i32) -> Self {
if let Some(interaction) = self.tree.world.ui.get_ui_node_interaction_mut(self.entity) {
interaction.tab_index = Some(index);
}
self
}
pub fn with_transition<S: UiStateTrait>(self, enter_speed: f32, exit_speed: f32) -> Self {
if let Some(weights) = self.tree.world.ui.get_ui_state_weights_mut(self.entity) {
weights.transitions[S::INDEX] = Some(StateTransition {
enter_speed,
exit_speed,
..Default::default()
});
}
self
}
pub fn with_eased_transition<S: UiStateTrait>(
self,
enter_speed: f32,
exit_speed: f32,
easing: EasingFunction,
) -> Self {
if let Some(weights) = self.tree.world.ui.get_ui_state_weights_mut(self.entity) {
weights.transitions[S::INDEX] = Some(StateTransition {
enter_speed,
exit_speed,
easing,
});
}
self
}
pub fn with_state_index_color(self, index: usize, color: Vec4) -> Self {
let capacity = index + 1;
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.ensure_state_capacity(capacity);
}
if let Some(node_color) = self.tree.world.ui.get_ui_node_color_mut(self.entity) {
node_color.ensure_state_capacity(capacity);
node_color.colors[index] = Some(color);
}
if let Some(weights) = self.tree.world.ui.get_ui_state_weights_mut(self.entity) {
weights.ensure_state_capacity(capacity);
}
if let Some(binding) = self.tree.world.ui.get_ui_theme_binding_mut(self.entity) {
binding.ensure_state_capacity(capacity);
}
self
}
pub fn with_state_index_transition(
self,
index: usize,
enter_speed: f32,
exit_speed: f32,
) -> Self {
let capacity = index + 1;
if let Some(weights) = self.tree.world.ui.get_ui_state_weights_mut(self.entity) {
weights.ensure_state_capacity(capacity);
weights.transitions[index] = Some(StateTransition {
enter_speed,
exit_speed,
..Default::default()
});
}
self
}
pub fn with_depth(self, depth: UiDepthMode) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.depth = depth;
}
self
}
pub fn with_font_size(self, size: f32) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.font_size = Some(size);
}
self
}
pub fn with_clip(self) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.clip_content = true;
}
self
}
pub fn with_visible(self, visible: bool) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.visible = visible;
}
self
}
pub fn without_pointer_events(self) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.pointer_events = false;
}
self
}
pub fn with_intro(self, animation: UiAnimationType, duration: f32) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
let anim = node.animation.get_or_insert(UiNodeAnimation {
intro: None,
outro: None,
duration,
progress: 0.0,
phase: UiAnimationPhase::Idle,
});
anim.intro = Some(animation);
anim.duration = duration;
if node.visible {
anim.phase = UiAnimationPhase::IntroPlaying;
anim.progress = 0.0;
}
}
self
}
pub fn with_outro(self, animation: UiAnimationType, duration: f32) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
let anim = node.animation.get_or_insert(UiNodeAnimation {
intro: None,
outro: None,
duration,
progress: 0.0,
phase: UiAnimationPhase::Idle,
});
anim.outro = Some(animation);
anim.duration = duration;
}
self
}
pub fn with_drag_source(self, payload: DragPayload) -> Self {
let this = if self
.tree
.world
.ui
.get_ui_node_interaction(self.entity)
.is_none()
{
self.with_interaction()
} else {
self
};
this.tree
.world
.ui
.set_ui_drag_source(this.entity, UiDragSource { payload });
this
}
pub fn with_drop_target(self) -> Self {
let this = if self
.tree
.world
.ui
.get_ui_node_interaction(self.entity)
.is_none()
{
self.with_interaction()
} else {
self
};
this.tree
.world
.ui
.set_ui_drop_target(this.entity, UiDropTarget::default());
this
}
pub fn with_filtered_drop_target(
self,
filter: crate::ecs::ui::components::DragAcceptFilter,
) -> Self {
let this = if self
.tree
.world
.ui
.get_ui_node_interaction(self.entity)
.is_none()
{
self.with_interaction()
} else {
self
};
this.tree.world.ui.set_ui_drop_target(
this.entity,
UiDropTarget {
accepted: true,
filter,
},
);
this
}
pub fn with_layer(self, layer: UiLayer) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.layer = Some(layer);
}
self
}
pub fn with_children(self, child_builder: impl FnOnce(&mut UiTreeBuilder<'a>)) -> Self {
let entity = self.entity;
let tree = self.tree;
tree.push_parent(entity);
child_builder(tree);
tree.pop_parent();
UiNodeBuilder { tree, entity }
}
pub fn with_responsive_flow(
self,
max_width: f32,
direction: crate::ecs::ui::layout_types::FlowDirection,
) -> Self {
if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
node.responsive_flow = Some(crate::ecs::ui::layout_types::ResponsiveFlowOverride {
max_width,
direction,
});
}
self
}
pub fn done(self) -> freecs::Entity {
self.entity
}
}