pub(crate) mod common;
pub(crate) mod leaf;
#[cfg(feature = "block_layout")]
pub(crate) mod block;
#[cfg(feature = "flexbox")]
pub(crate) mod flexbox;
#[cfg(feature = "grid")]
pub(crate) mod grid;
pub use leaf::compute_leaf_layout;
#[cfg(feature = "block_layout")]
pub use self::block::compute_block_layout;
#[cfg(feature = "flexbox")]
pub use self::flexbox::compute_flexbox_layout;
#[cfg(feature = "grid")]
pub use self::grid::compute_grid_layout;
use crate::geometry::{Line, Point, Size};
use crate::style::{AvailableSpace, CoreStyle, Overflow};
use crate::tree::{
Layout, LayoutInput, LayoutOutput, LayoutPartialTree, LayoutPartialTreeExt, NodeId, RoundTree, SizingMode,
};
use crate::util::debug::{debug_log, debug_log_node, debug_pop_node, debug_push_node};
use crate::util::sys::round;
use crate::util::ResolveOrZero;
use crate::{BoxSizing, CacheTree, MaybeMath, MaybeResolve};
pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, available_space: Size<AvailableSpace>) {
let mut known_dimensions = Size::NONE;
#[cfg(feature = "block_layout")]
{
let parent_size = available_space.into_options();
let style = tree.get_core_container_style(root);
if style.is_block() {
let aspect_ratio = style.aspect_ratio();
let margin = style.margin().resolve_or_zero(parent_size.width);
let padding = style.padding().resolve_or_zero(parent_size.width);
let border = style.border().resolve_or_zero(parent_size.width);
let padding_border_size = (padding + border).sum_axes();
let box_sizing_adjustment =
if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
let min_size = style
.min_size()
.maybe_resolve(parent_size)
.maybe_apply_aspect_ratio(aspect_ratio)
.maybe_add(box_sizing_adjustment);
let max_size = style
.max_size()
.maybe_resolve(parent_size)
.maybe_apply_aspect_ratio(aspect_ratio)
.maybe_add(box_sizing_adjustment);
let clamped_style_size = style
.size()
.maybe_resolve(parent_size)
.maybe_apply_aspect_ratio(aspect_ratio)
.maybe_add(box_sizing_adjustment)
.maybe_clamp(min_size, max_size);
let min_max_definite_size = min_size.zip_map(max_size, |min, max| match (min, max) {
(Some(min), Some(max)) if max <= min => Some(min),
_ => None,
});
let available_space_based_size = Size {
width: available_space.width.into_option().maybe_sub(margin.horizontal_axis_sum()),
height: None,
};
let styled_based_known_dimensions = known_dimensions
.or(min_max_definite_size)
.or(clamped_style_size)
.or(available_space_based_size)
.maybe_max(padding_border_size);
known_dimensions = styled_based_known_dimensions;
}
}
let output = tree.perform_child_layout(
root,
known_dimensions,
available_space.into_options(),
available_space,
SizingMode::InherentSize,
Line::FALSE,
);
let style = tree.get_core_container_style(root);
let padding = style.padding().resolve_or_zero(available_space.width.into_option());
let border = style.border().resolve_or_zero(available_space.width.into_option());
let margin = style.margin().resolve_or_zero(available_space.width.into_option());
let scrollbar_size = Size {
width: if style.overflow().y == Overflow::Scroll { style.scrollbar_width() } else { 0.0 },
height: if style.overflow().x == Overflow::Scroll { style.scrollbar_width() } else { 0.0 },
};
drop(style);
tree.set_unrounded_layout(
root,
&Layout {
order: 0,
location: Point::ZERO,
size: output.size,
#[cfg(feature = "content_size")]
content_size: output.content_size,
scrollbar_size,
padding,
border,
margin,
},
);
}
#[inline(always)]
pub fn compute_cached_layout<Tree: CacheTree + ?Sized, ComputeFunction>(
tree: &mut Tree,
node: NodeId,
inputs: LayoutInput,
mut compute_uncached: ComputeFunction,
) -> LayoutOutput
where
ComputeFunction: FnMut(&mut Tree, NodeId, LayoutInput) -> LayoutOutput,
{
debug_push_node!(node);
let LayoutInput { known_dimensions, available_space, run_mode, .. } = inputs;
let cache_entry = tree.cache_get(node, known_dimensions, available_space, run_mode);
if let Some(cached_size_and_baselines) = cache_entry {
debug_log_node!(known_dimensions, inputs.parent_size, available_space, run_mode, inputs.sizing_mode);
debug_log!("RESULT (CACHED)", dbg:cached_size_and_baselines.size);
debug_pop_node!();
return cached_size_and_baselines;
}
debug_log_node!(known_dimensions, inputs.parent_size, available_space, run_mode, inputs.sizing_mode);
let computed_size_and_baselines = compute_uncached(tree, node, inputs);
tree.cache_store(node, known_dimensions, available_space, run_mode, computed_size_and_baselines);
debug_log!("RESULT", dbg:computed_size_and_baselines.size);
debug_pop_node!();
computed_size_and_baselines
}
pub fn round_layout(tree: &mut impl RoundTree, node_id: NodeId) {
return round_layout_inner(tree, node_id, 0.0, 0.0);
fn round_layout_inner(tree: &mut impl RoundTree, node_id: NodeId, cumulative_x: f32, cumulative_y: f32) {
let unrounded_layout = *tree.get_unrounded_layout(node_id);
let mut layout = unrounded_layout;
let cumulative_x = cumulative_x + unrounded_layout.location.x;
let cumulative_y = cumulative_y + unrounded_layout.location.y;
layout.location.x = round(unrounded_layout.location.x);
layout.location.y = round(unrounded_layout.location.y);
layout.size.width = round(cumulative_x + unrounded_layout.size.width) - round(cumulative_x);
layout.size.height = round(cumulative_y + unrounded_layout.size.height) - round(cumulative_y);
layout.scrollbar_size.width = round(unrounded_layout.scrollbar_size.width);
layout.scrollbar_size.height = round(unrounded_layout.scrollbar_size.height);
layout.border.left = round(cumulative_x + unrounded_layout.border.left) - round(cumulative_x);
layout.border.right = round(cumulative_x + unrounded_layout.size.width)
- round(cumulative_x + unrounded_layout.size.width - unrounded_layout.border.right);
layout.border.top = round(cumulative_y + unrounded_layout.border.top) - round(cumulative_y);
layout.border.bottom = round(cumulative_y + unrounded_layout.size.height)
- round(cumulative_y + unrounded_layout.size.height - unrounded_layout.border.bottom);
layout.padding.left = round(cumulative_x + unrounded_layout.padding.left) - round(cumulative_x);
layout.padding.right = round(cumulative_x + unrounded_layout.size.width)
- round(cumulative_x + unrounded_layout.size.width - unrounded_layout.padding.right);
layout.padding.top = round(cumulative_y + unrounded_layout.padding.top) - round(cumulative_y);
layout.padding.bottom = round(cumulative_y + unrounded_layout.size.height)
- round(cumulative_y + unrounded_layout.size.height - unrounded_layout.padding.bottom);
#[cfg(feature = "content_size")]
round_content_size(&mut layout, unrounded_layout.content_size, cumulative_x, cumulative_y);
tree.set_final_layout(node_id, &layout);
let child_count = tree.child_count(node_id);
for index in 0..child_count {
let child = tree.get_child_id(node_id, index);
round_layout_inner(tree, child, cumulative_x, cumulative_y);
}
}
#[cfg(feature = "content_size")]
#[inline(always)]
fn round_content_size(
layout: &mut Layout,
unrounded_content_size: Size<f32>,
cumulative_x: f32,
cumulative_y: f32,
) {
layout.content_size.width = round(cumulative_x + unrounded_content_size.width) - round(cumulative_x);
layout.content_size.height = round(cumulative_y + unrounded_content_size.height) - round(cumulative_y);
}
}
pub fn compute_hidden_layout(tree: &mut (impl LayoutPartialTree + CacheTree), node: NodeId) -> LayoutOutput {
tree.cache_clear(node);
tree.set_unrounded_layout(node, &Layout::with_order(0));
for index in 0..tree.child_count(node) {
let child_id = tree.get_child_id(node, index);
tree.compute_child_layout(child_id, LayoutInput::HIDDEN);
}
LayoutOutput::HIDDEN
}
#[cfg(test)]
mod tests {
use super::compute_hidden_layout;
use crate::geometry::{Point, Size};
use crate::style::{Display, Style};
use crate::TaffyTree;
#[test]
fn hidden_layout_should_hide_recursively() {
let mut taffy: TaffyTree<()> = TaffyTree::new();
let style: Style = Style { display: Display::Flex, size: Size::from_lengths(50.0, 50.0), ..Default::default() };
let grandchild_00 = taffy.new_leaf(style.clone()).unwrap();
let grandchild_01 = taffy.new_leaf(style.clone()).unwrap();
let child_00 = taffy.new_with_children(style.clone(), &[grandchild_00, grandchild_01]).unwrap();
let grandchild_02 = taffy.new_leaf(style.clone()).unwrap();
let child_01 = taffy.new_with_children(style.clone(), &[grandchild_02]).unwrap();
let root = taffy
.new_with_children(
Style { display: Display::None, size: Size::from_lengths(50.0, 50.0), ..Default::default() },
&[child_00, child_01],
)
.unwrap();
compute_hidden_layout(&mut taffy.as_layout_tree(), root);
for node in [root, child_00, child_01, grandchild_00, grandchild_01, grandchild_02] {
let layout = taffy.layout(node).unwrap();
assert_eq!(layout.size, Size::zero());
assert_eq!(layout.location, Point::zero());
}
}
}