use bevy_ecs::hierarchy::{ChildOf, Children};
use bevy_ecs::name::Name;
use bevy_ecs::prelude::*;
use tracing::{debug, trace};
use super::super::metrics::{LayoutScale, Offset, Size};
use super::super::taffy::{TaffyComputedLayout, TaffyLayoutResource, TaffyStyle};
use super::super::{Arrangement, ArrangementTreeChanged, BoxStyle, Dimension, LayoutRoot};
use crate::ecs::graphics::format_entity_name;
use crate::ecs::window::DPI;
use taffy::prelude::*;
pub fn build_taffy_styles_system(
mut commands: Commands,
without_style: Query<
(Entity, Option<&BoxStyle>),
(Or<(With<LayoutRoot>, With<BoxStyle>)>, Without<TaffyStyle>),
>,
mut changed: Query<(&BoxStyle, &mut TaffyStyle), Changed<BoxStyle>>,
) {
for (entity, box_style) in without_style.iter() {
let taffy_style: taffy::Style = box_style.map(|s| s.into()).unwrap_or_default();
commands.entity(entity).insert((
TaffyStyle(taffy_style),
TaffyComputedLayout::default(),
ArrangementTreeChanged,
));
}
for (box_style, mut taffy_style) in changed.iter_mut() {
taffy_style.0 = box_style.into();
}
}
pub fn sync_taffy_tree_system(
mut taffy_res: ResMut<TaffyLayoutResource>,
new_entities: Query<Entity, Added<TaffyStyle>>,
changed_styles: Query<(Entity, &TaffyStyle), Changed<TaffyStyle>>,
changed_hierarchy: Query<&ChildOf, Changed<ChildOf>>,
mut removed_hierarchy: RemovedComponents<ChildOf>,
children_query: Query<&Children>,
) {
for entity in new_entities.iter() {
if taffy_res.get_node(entity).is_none() {
let _ = taffy_res.create_node(entity);
}
}
for (entity, style) in changed_styles.iter() {
if let Some(node_id) = taffy_res.get_node(entity) {
let _ = taffy_res.taffy_mut().set_style(node_id, style.0.clone());
}
}
let mut affected_parents = std::collections::HashSet::new();
for child_of in changed_hierarchy.iter() {
affected_parents.insert(child_of.parent());
}
for parent_entity in affected_parents {
if let Some(parent_node) = taffy_res.get_node(parent_entity) {
if let Ok(children) = children_query.get(parent_entity) {
let ordered_node_ids: Vec<taffy::NodeId> = children
.iter()
.filter_map(|child_entity| taffy_res.get_node(child_entity))
.collect();
let _ = taffy_res
.taffy_mut()
.set_children(parent_node, &ordered_node_ids);
}
}
}
for entity in removed_hierarchy.read() {
if let Some(node_id) = taffy_res.get_node(entity) {
if let Some(parent_node) = taffy_res.taffy().parent(node_id) {
let _ = taffy_res.taffy_mut().remove_child(parent_node, node_id);
}
}
}
}
pub fn compute_taffy_layout_system(
mut taffy_res: ResMut<TaffyLayoutResource>,
roots: Query<(Entity, Option<&BoxStyle>), With<LayoutRoot>>,
changed_styles: Query<(), Changed<TaffyStyle>>,
changed_box_styles: Query<(), Changed<BoxStyle>>,
changed_hierarchy: Query<(), Changed<ChildOf>>,
mut all_taffy_entities: Query<(Entity, &mut TaffyComputedLayout), With<TaffyStyle>>,
) {
let has_changes = !changed_styles.is_empty()
|| !changed_box_styles.is_empty()
|| !changed_hierarchy.is_empty();
if has_changes {
for (root_entity, box_style) in roots.iter() {
if let Some(root_node) = taffy_res.get_node(root_entity) {
let to_available = |d: Option<Dimension>| match d {
Some(Dimension::Px(px)) => AvailableSpace::Definite(px),
_ => AvailableSpace::MaxContent,
};
let root_size = box_style.and_then(|style| style.size);
let available_space = taffy::Size {
width: to_available(root_size.and_then(|s| s.width)),
height: to_available(root_size.and_then(|s| s.height)),
};
let result = taffy_res
.taffy_mut()
.compute_layout(root_node, available_space);
if result.is_ok() {
for (entity, mut computed) in all_taffy_entities.iter_mut() {
if let Some(node_id) = taffy_res.get_node(entity) {
if let Ok(layout) = taffy_res.taffy().layout(node_id) {
let new_layout = TaffyComputedLayout(*layout);
if *computed != new_layout {
tracing::debug!(
entity = ?entity,
old_width = computed.0.size.width,
old_height = computed.0.size.height,
new_width = new_layout.0.size.width,
new_height = new_layout.0.size.height,
"[compute_taffy_layout] Layout changed"
);
*computed = new_layout;
}
}
}
}
}
}
}
}
}
pub fn update_arrangements_system(
mut commands: Commands,
mut query: Query<
(
Entity,
&TaffyComputedLayout,
Option<&mut Arrangement>,
Option<&Name>,
Option<Ref<DPI>>,
Option<&crate::ecs::window::Window>,
),
(
Or<(Changed<TaffyComputedLayout>, Changed<DPI>)>,
With<TaffyStyle>,
),
>,
) {
for (entity, computed_layout, arrangement, name, dpi, window) in query.iter_mut() {
let layout = &computed_layout.0;
let is_window = window.is_some();
let scale = if is_window {
if let Some(ref d) = dpi {
LayoutScale {
x: d.scale_x(),
y: d.scale_y(),
}
} else {
LayoutScale::default() }
} else {
LayoutScale::default() };
if let Some(ref d) = dpi {
if d.is_changed() {
debug!(
entity = ?entity,
dpi_x = d.dpi_x,
dpi_y = d.dpi_y,
scale_x = scale.x,
scale_y = scale.y,
is_window = is_window,
"[update_arrangements] DPI changed, scale applied"
);
}
}
let new_offset = if is_window {
arrangement
.as_ref()
.map_or(Offset { x: 0.0, y: 0.0 }, |arr| arr.offset)
} else {
Offset {
x: layout.location.x,
y: layout.location.y,
}
};
let new_arrangement = Arrangement {
offset: new_offset,
scale,
size: Size {
width: layout.size.width,
height: layout.size.height,
},
};
let entity_name = format_entity_name(entity, name);
trace!(
entity = %entity_name,
location_x = layout.location.x,
location_y = layout.location.y,
size_width = layout.size.width,
size_height = layout.size.height,
"[update_arrangements] TaffyLayout"
);
trace!(
entity = %entity_name,
offset_x = new_arrangement.offset.x,
offset_y = new_arrangement.offset.y,
scale_x = new_arrangement.scale.x,
scale_y = new_arrangement.scale.y,
size_width = new_arrangement.size.width,
size_height = new_arrangement.size.height,
"[update_arrangements] Arrangement"
);
if let Some(mut arr) = arrangement {
arr.set_if_neq(new_arrangement);
} else {
commands.entity(entity).insert(new_arrangement);
}
}
}
pub fn cleanup_removed_entities_system(
mut taffy_res: ResMut<TaffyLayoutResource>,
mut removed: RemovedComponents<TaffyStyle>,
) {
for entity in removed.read() {
let _ = taffy_res.remove_node(entity);
}
}