ui: cleaner stretch integration
This commit is contained in:
parent
93bb1d5b8e
commit
339e9ad52d
@ -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<Mesh>, // TODO: maybe abstract this out
|
||||
pub material: Handle<ColorMaterial>,
|
||||
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(),
|
||||
|
||||
@ -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<FlexSurfaceId, FlexSurface>,
|
||||
}
|
||||
|
||||
pub struct FlexSurface {
|
||||
entity_to_stretch: HashMap<Entity, stretch::node::Node>,
|
||||
stretch_to_entity: HashMap<stretch::node::Node, Entity>,
|
||||
surface_root_node: stretch::node::Node,
|
||||
size: Vec2,
|
||||
window_nodes: HashMap<WindowId, stretch::node::Node>,
|
||||
stretch: Stretch,
|
||||
orphans: HashSet<Entity>,
|
||||
}
|
||||
|
||||
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<Item = Entity>,
|
||||
) {
|
||||
let stretch_node = self.window_nodes.get(&window_id).unwrap();
|
||||
let child_nodes = children
|
||||
.map(|e| *self.entity_to_stretch.get(&e).unwrap())
|
||||
.collect::<Vec<stretch::node::Node>>();
|
||||
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<Windows>,
|
||||
mut flex_surfaces: ResMut<FlexSurfaces>,
|
||||
) {
|
||||
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<FlexSurfaces>,
|
||||
mut root_node_query: Query<With<Node, Without<Parent, (&FlexSurfaceId, &mut Style)>>>,
|
||||
mut node_query: Query<With<Node, (Entity, &FlexSurfaceId, Changed<Style>, Option<&Parent>)>>,
|
||||
mut children_query: Query<With<Node, (Entity, &FlexSurfaceId, Changed<Children>)>>,
|
||||
mut node_transform_query: Query<(
|
||||
Entity,
|
||||
&mut Node,
|
||||
&FlexSurfaceId,
|
||||
&mut LocalTransform,
|
||||
Option<&Parent>,
|
||||
)>,
|
||||
windows: Res<Windows>,
|
||||
mut flex_surface: ResMut<FlexSurface>,
|
||||
mut root_node_query: Query<With<Node, Without<Parent, Entity>>>,
|
||||
mut node_query: Query<With<Node, (Entity, Changed<Style>)>>,
|
||||
mut children_query: Query<With<Node, (Entity, Changed<Children>)>>,
|
||||
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;
|
||||
}
|
||||
|
||||
@ -38,15 +38,11 @@ pub struct UiPlugin;
|
||||
|
||||
impl AppPlugin for UiPlugin {
|
||||
fn build(&self, app: &mut AppBuilder) {
|
||||
app.init_resource::<FlexSurfaces>()
|
||||
app.init_resource::<FlexSurface>()
|
||||
.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::<RenderGraph>().unwrap();
|
||||
render_graph.add_ui_graph(resources);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<With<Node, Without<Parent, (Entity, &FlexSurfaceId)>>>,
|
||||
mut node_query: Query<(Entity, &Node, &mut FlexSurfaceId, &mut LocalTransform)>,
|
||||
mut root_node_query: Query<With<Node, Without<Parent, Entity>>>,
|
||||
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::<Vec<(Entity, FlexSurfaceId)>>();
|
||||
.collect::<Vec<Entity>>();
|
||||
|
||||
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::<FlexSurfaceId>(entity).unwrap();
|
||||
_parent_result: Option<f32>,
|
||||
previous_result: Option<f32>,
|
||||
) -> Option<f32> {
|
||||
let mut transform = node_query.get_mut::<LocalTransform>(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);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user