From 339e9ad52d8250de0f3d9863ec523f53e44e20f0 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 25 Jul 2020 13:14:36 -0700 Subject: [PATCH] ui: cleaner stretch integration --- crates/bevy_ui/src/entity.rs | 8 +- crates/bevy_ui/src/flex.rs | 182 +++++++++++++++-------------------- crates/bevy_ui/src/lib.rs | 8 +- crates/bevy_ui/src/update.rs | 32 +++--- 4 files changed, 95 insertions(+), 135 deletions(-) diff --git a/crates/bevy_ui/src/entity.rs b/crates/bevy_ui/src/entity.rs index 336679259b..4aeca30a48 100644 --- a/crates/bevy_ui/src/entity.rs +++ b/crates/bevy_ui/src/entity.rs @@ -3,7 +3,7 @@ use crate::{ prelude::Flex, render::UI_PIPELINE_HANDLE, widget::{Button, Text}, - Click, FlexSurfaceId, FocusPolicy, Hover, + Click, FocusPolicy, Hover, }; use bevy_asset::Handle; use bevy_ecs::Bundle; @@ -23,7 +23,6 @@ use bevy_transform::{ pub struct NodeComponents { pub node: Node, pub flex: Flex, - pub flex_surface_id: FlexSurfaceId, pub mesh: Handle, // TODO: maybe abstract this out pub material: Handle, pub draw: Draw, @@ -55,7 +54,6 @@ impl Default for NodeComponents { }, )]), node: Default::default(), - flex_surface_id: Default::default(), flex: Flex::default(), material: Default::default(), draw: Default::default(), @@ -69,7 +67,6 @@ impl Default for NodeComponents { pub struct TextComponents { pub node: Node, pub flex: Flex, - pub flex_surface_id: FlexSurfaceId, pub draw: Draw, pub text: Text, pub focus_policy: FocusPolicy, @@ -83,7 +80,6 @@ impl Default for TextComponents { text: Text::default(), node: Default::default(), flex: Flex::default(), - flex_surface_id: Default::default(), focus_policy: FocusPolicy::Pass, draw: Draw { is_transparent: true, @@ -100,7 +96,6 @@ pub struct ButtonComponents { pub node: Node, pub button: Button, pub flex: Flex, - pub flex_surface_id: FlexSurfaceId, pub click: Click, pub hover: Hover, pub focus_policy: FocusPolicy, @@ -139,7 +134,6 @@ impl Default for ButtonComponents { }, )]), node: Default::default(), - flex_surface_id: Default::default(), flex: Flex::default(), material: Default::default(), draw: Default::default(), diff --git a/crates/bevy_ui/src/flex.rs b/crates/bevy_ui/src/flex.rs index 077d5235c8..e62543f707 100644 --- a/crates/bevy_ui/src/flex.rs +++ b/crates/bevy_ui/src/flex.rs @@ -2,59 +2,35 @@ use crate::Node; use bevy_ecs::{Changed, Entity, Query, Res, ResMut, With, Without}; use bevy_math::Vec2; use bevy_transform::prelude::{Children, LocalTransform, Parent}; -use bevy_window::Windows; -use std::collections::{HashMap, HashSet}; +use bevy_window::{Window, WindowId, Windows}; +use std::collections::HashMap; use stretch::{ geometry::Size, - number::Number, result::Layout, - style::{Dimension, PositionType, Style}, + style::{Dimension, Style}, Stretch, }; -#[derive(Default, Debug, Copy, Clone, Hash, Eq, PartialEq)] -pub struct FlexSurfaceId(usize); - -#[derive(Default)] -pub struct FlexSurfaces { - surfaces: HashMap, -} - pub struct FlexSurface { entity_to_stretch: HashMap, stretch_to_entity: HashMap, - surface_root_node: stretch::node::Node, - size: Vec2, + window_nodes: HashMap, stretch: Stretch, - orphans: HashSet, } -impl FlexSurface { - fn new() -> Self { - let mut stretch = Stretch::new(); - let surface_root_node = stretch - .new_node( - Style { - size: Size { - width: Dimension::Percent(1.0), - height: Dimension::Percent(1.0), - }, - ..Default::default() - }, - Vec::new(), - ) - .unwrap(); +impl Default for FlexSurface { + fn default() -> Self { Self { entity_to_stretch: Default::default(), stretch_to_entity: Default::default(), - orphans: Default::default(), - size: Default::default(), - stretch, - surface_root_node, + window_nodes: Default::default(), + stretch: Stretch::new(), } } +} - pub fn upsert_node(&mut self, entity: Entity, style: &Style, orphan: bool) { +impl FlexSurface { + pub fn upsert_node(&mut self, entity: Entity, style: &Style) { let mut added = false; let stretch = &mut self.stretch; let stretch_to_entity = &mut self.stretch_to_entity; @@ -70,18 +46,6 @@ impl FlexSurface { .set_style(*stretch_node, style.clone()) .unwrap(); } - - if orphan && !self.orphans.contains(&entity) { - self.stretch - .add_child(self.surface_root_node, *stretch_node) - .unwrap(); - self.orphans.insert(entity); - } else if !orphan && self.orphans.contains(&entity) { - self.stretch - .remove_child(self.surface_root_node, *stretch_node) - .unwrap(); - self.orphans.remove(&entity); - } } pub fn update_children(&mut self, entity: Entity, children: &Children) { @@ -92,24 +56,60 @@ impl FlexSurface { } let stretch_node = self.entity_to_stretch.get(&entity).unwrap(); - self.stretch .set_children(*stretch_node, stretch_children) .unwrap(); } - pub fn compute_layout(&mut self) { - self.stretch - .compute_layout( - self.surface_root_node, - stretch::geometry::Size { - width: Number::Defined(self.size.x()), - height: Number::Defined(self.size.y()), + pub fn update_window(&mut self, window: &Window) { + let stretch = &mut self.stretch; + let node = self.window_nodes.entry(window.id).or_insert_with(|| { + stretch + .new_node( + Style { + ..Default::default() + }, + Vec::new(), + ) + .unwrap() + }); + + stretch + .set_style( + *node, + Style { + size: Size { + width: Dimension::Points(window.width as f32), + height: Dimension::Points(window.height as f32), + }, + ..Default::default() }, ) .unwrap(); } + pub fn set_window_children( + &mut self, + window_id: WindowId, + children: impl Iterator, + ) { + let stretch_node = self.window_nodes.get(&window_id).unwrap(); + let child_nodes = children + .map(|e| *self.entity_to_stretch.get(&e).unwrap()) + .collect::>(); + self.stretch + .set_children(*stretch_node, child_nodes) + .unwrap(); + } + + pub fn compute_window_layouts(&mut self) { + for window_node in self.window_nodes.values() { + self.stretch + .compute_layout(*window_node, stretch::geometry::Size::undefined()) + .unwrap(); + } + } + pub fn get_layout(&self, entity: Entity) -> Result<&Layout, stretch::Error> { let stretch_node = self.entity_to_stretch.get(&entity).unwrap(); self.stretch.layout(*stretch_node) @@ -117,75 +117,51 @@ impl FlexSurface { } // SAFE: as long as MeasureFunc is Send + Sync. https://github.com/vislyhq/stretch/issues/69 -unsafe impl Send for FlexSurfaces {} -unsafe impl Sync for FlexSurfaces {} - -pub fn primary_window_flex_surface_system( - windows: Res, - mut flex_surfaces: ResMut, -) { - if let Some(surface) = flex_surfaces.surfaces.get_mut(&FlexSurfaceId::default()) { - if let Some(window) = windows.get_primary() { - surface.size = Vec2::new(window.width as f32, window.height as f32); - } - } -} +unsafe impl Send for FlexSurface {} +unsafe impl Sync for FlexSurface {} pub fn flex_node_system( - mut flex_surfaces: ResMut, - mut root_node_query: Query>>, - mut node_query: Query, Option<&Parent>)>>, - mut children_query: Query)>>, - mut node_transform_query: Query<( - Entity, - &mut Node, - &FlexSurfaceId, - &mut LocalTransform, - Option<&Parent>, - )>, + windows: Res, + mut flex_surface: ResMut, + mut root_node_query: Query>>, + mut node_query: Query)>>, + mut children_query: Query)>>, + mut node_transform_query: Query<(Entity, &mut Node, &mut LocalTransform, Option<&Parent>)>, ) { - // initialize stretch hierarchies - for (flex_surface_id, mut style) in &mut root_node_query.iter() { - flex_surfaces - .surfaces - .entry(*flex_surface_id) - .or_insert_with(|| FlexSurface::new()); - - // root nodes should not be positioned relative to each other - style.position_type = PositionType::Absolute; + // update window root nodes + for window in windows.iter() { + flex_surface.update_window(window); } - // TODO: cleanup unused surfaces - // update changed nodes - for (entity, flex_surface_id, style, parent) in &mut node_query.iter() { + for (entity, style) in &mut node_query.iter() { // TODO: remove node from old hierarchy if its root has changed - let surface = flex_surfaces.surfaces.get_mut(flex_surface_id).unwrap(); - surface.upsert_node(entity, &style, parent.is_none()); + flex_surface.upsert_node(entity, &style); } // TODO: handle removed nodes + // update window children (for now assuming all Nodes live in the primary window) + if let Some(primary_window) = windows.get_primary() { + flex_surface.set_window_children(primary_window.id, root_node_query.iter().iter()); + } + // update children - for (entity, flex_surface_id, children) in &mut children_query.iter() { - let surface = flex_surfaces.surfaces.get_mut(flex_surface_id).unwrap(); - surface.update_children(entity, &children); + for (entity, children) in &mut children_query.iter() { + flex_surface.update_children(entity, &children); } // compute layouts - for surface in flex_surfaces.surfaces.values_mut() { - surface.compute_layout(); - } + flex_surface.compute_window_layouts(); - for (entity, mut node, flex_surface_id, mut local, parent) in &mut node_transform_query.iter() { - let surface = flex_surfaces.surfaces.get_mut(flex_surface_id).unwrap(); - let layout = surface.get_layout(entity).unwrap(); + for (entity, mut node, mut local, parent) in &mut node_transform_query.iter() { + let layout = flex_surface.get_layout(entity).unwrap(); node.size = Vec2::new(layout.size.width, layout.size.height); let mut position = local.w_axis(); position.set_x(layout.location.x + layout.size.width / 2.0); position.set_y(layout.location.y + layout.size.height / 2.0); if let Some(parent) = parent { - if let Ok(parent_layout) = surface.get_layout(parent.0) { + if let Ok(parent_layout) = flex_surface.get_layout(parent.0) { *position.x_mut() -= parent_layout.size.width / 2.0; *position.y_mut() -= parent_layout.size.height / 2.0; } diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index f4e10ba5b9..4618794e51 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -38,15 +38,11 @@ pub struct UiPlugin; impl AppPlugin for UiPlugin { fn build(&self, app: &mut AppBuilder) { - app.init_resource::() + app.init_resource::() .add_system_to_stage(stage::PRE_UPDATE, ui_focus_system.system()) // add these stages to front because these must run before transform update systems .add_system_to_stage_front(stage::POST_UPDATE, flex_node_system.system()) .add_system_to_stage_front(stage::POST_UPDATE, ui_z_system.system()) - .add_system_to_stage_front( - stage::POST_UPDATE, - primary_window_flex_surface_system.system(), - ) .add_system_to_stage(stage::POST_UPDATE, widget::text_system.system()) .add_system_to_stage(bevy_render::stage::DRAW, widget::draw_text_system.system()); @@ -54,4 +50,4 @@ impl AppPlugin for UiPlugin { let mut render_graph = resources.get_mut::().unwrap(); render_graph.add_ui_graph(resources); } -} +} \ No newline at end of file diff --git a/crates/bevy_ui/src/update.rs b/crates/bevy_ui/src/update.rs index af2171fdd7..4d041c199a 100644 --- a/crates/bevy_ui/src/update.rs +++ b/crates/bevy_ui/src/update.rs @@ -1,5 +1,4 @@ use super::Node; -use crate::FlexSurfaceId; use bevy_ecs::{Entity, Query, With, Without}; use bevy_transform::{ hierarchy, @@ -9,8 +8,8 @@ use bevy_transform::{ pub const UI_Z_STEP: f32 = 0.001; pub fn ui_z_system( - mut root_node_query: Query>>, - mut node_query: Query<(Entity, &Node, &mut FlexSurfaceId, &mut LocalTransform)>, + mut root_node_query: Query>>, + mut node_query: Query<(Entity, &Node, &mut LocalTransform)>, children_query: Query<&Children>, ) { let mut window_z = 0.0; @@ -18,34 +17,31 @@ pub fn ui_z_system( // PERF: we can probably avoid an allocation here by making root_node_query and node_query non-overlapping let root_nodes = (&mut root_node_query.iter()) .iter() - .map(|(e, s)| (e, *s)) - .collect::>(); + .collect::>(); - for (entity, flex_surface_id) in root_nodes { + for entity in root_nodes { if let Some(result) = hierarchy::run_on_hierarchy( &children_query, &mut node_query, entity, - Some((flex_surface_id, window_z)), - Some((flex_surface_id, window_z)), + Some(window_z), + Some(window_z), &mut update_node_entity, ) { - window_z = result.1; + window_z = result; } } } fn update_node_entity( - node_query: &mut Query<(Entity, &Node, &mut FlexSurfaceId, &mut LocalTransform)>, + node_query: &mut Query<(Entity, &Node, &mut LocalTransform)>, entity: Entity, - parent_result: Option<(FlexSurfaceId, f32)>, - previous_result: Option<(FlexSurfaceId, f32)>, -) -> Option<(FlexSurfaceId, f32)> { - let mut surface_id = node_query.get_mut::(entity).unwrap(); + _parent_result: Option, + previous_result: Option, +) -> Option { let mut transform = node_query.get_mut::(entity).unwrap(); - let (parent_surface_id, _) = parent_result?; let mut z = UI_Z_STEP; - if let Some((_, previous_z)) = previous_result { + if let Some(previous_z) = previous_result { z += previous_z; }; @@ -53,7 +49,5 @@ fn update_node_entity( position.set_z(z); transform.set_w_axis(position); - *surface_id = parent_surface_id; - - return Some((parent_surface_id, z)); + return Some(z); }