pub(crate) mod common;
pub(crate) mod flexbox;
pub(crate) mod leaf;
#[cfg(feature = "grid")]
pub(crate) mod grid;
use crate::data::CACHE_SIZE;
use crate::error::TaffyError;
use crate::geometry::{Point, Size};
use crate::layout::{Cache, Layout, RunMode, SizeAndBaselines, SizingMode};
use crate::node::{Node, Taffy};
use crate::style::{AvailableSpace, Display};
use crate::sys::round;
use crate::tree::LayoutTree;
use self::flexbox::FlexboxAlgorithm;
#[cfg(feature = "grid")]
use self::grid::CssGridAlgorithm;
use self::leaf::LeafAlgorithm;
#[cfg(any(feature = "debug", feature = "profile"))]
use crate::debug::NODE_LOGGER;
pub fn compute_layout(taffy: &mut Taffy, root: Node, available_space: Size<AvailableSpace>) -> Result<(), TaffyError> {
taffy.is_layouting = true;
let size_and_baselines = GenericAlgorithm::perform_layout(
taffy,
root,
Size::NONE,
available_space.into_options(),
available_space,
SizingMode::InherentSize,
);
let layout = Layout { order: 0, size: size_and_baselines.size, location: Point::ZERO };
*taffy.layout_mut(root) = layout;
if taffy.config.use_rounding {
round_layout(taffy, root, 0.0, 0.0);
}
taffy.is_layouting = false;
Ok(())
}
pub(crate) trait LayoutAlgorithm {
const NAME: &'static str;
fn measure_size(
tree: &mut impl LayoutTree,
node: Node,
known_dimensions: Size<Option<f32>>,
parent_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
sizing_mode: SizingMode,
) -> Size<f32>;
fn perform_layout(
tree: &mut impl LayoutTree,
node: Node,
known_dimensions: Size<Option<f32>>,
parent_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
sizing_mode: SizingMode,
) -> SizeAndBaselines;
}
pub struct GenericAlgorithm;
impl LayoutAlgorithm for GenericAlgorithm {
const NAME: &'static str = "GENERIC";
fn perform_layout(
tree: &mut impl LayoutTree,
node: Node,
known_dimensions: Size<Option<f32>>,
parent_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
sizing_mode: SizingMode,
) -> SizeAndBaselines {
compute_node_layout(
tree,
node,
known_dimensions,
parent_size,
available_space,
RunMode::PeformLayout,
sizing_mode,
)
}
fn measure_size(
tree: &mut impl LayoutTree,
node: Node,
known_dimensions: Size<Option<f32>>,
parent_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
sizing_mode: SizingMode,
) -> Size<f32> {
compute_node_layout(
tree,
node,
known_dimensions,
parent_size,
available_space,
RunMode::ComputeSize,
sizing_mode,
)
.size
}
}
fn compute_node_layout(
tree: &mut impl LayoutTree,
node: Node,
known_dimensions: Size<Option<f32>>,
parent_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
run_mode: RunMode,
sizing_mode: SizingMode,
) -> SizeAndBaselines {
#[cfg(any(feature = "debug", feature = "profile"))]
NODE_LOGGER.push_node(node);
#[cfg(feature = "debug")]
println!();
let cache_run_mode = if tree.is_childless(node) { RunMode::PeformLayout } else { run_mode };
if let Some(cached_size_and_baselines) =
compute_from_cache(tree, node, known_dimensions, available_space, cache_run_mode)
{
#[cfg(feature = "debug")]
NODE_LOGGER.labelled_debug_log("CACHE", cached_size_and_baselines.size);
#[cfg(feature = "debug")]
debug_log_node(known_dimensions, parent_size, available_space, run_mode, sizing_mode);
#[cfg(any(feature = "debug", feature = "profile"))]
NODE_LOGGER.pop_node();
return cached_size_and_baselines;
}
#[cfg(feature = "debug")]
debug_log_node(known_dimensions, parent_size, available_space, run_mode, sizing_mode);
#[inline(always)]
fn perform_computations<Algorithm: LayoutAlgorithm>(
tree: &mut impl LayoutTree,
node: Node,
known_dimensions: Size<Option<f32>>,
parent_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
run_mode: RunMode,
sizing_mode: SizingMode,
) -> SizeAndBaselines {
#[cfg(feature = "debug")]
NODE_LOGGER.log(Algorithm::NAME);
match run_mode {
RunMode::PeformLayout => {
Algorithm::perform_layout(tree, node, known_dimensions, parent_size, available_space, sizing_mode)
}
RunMode::ComputeSize => {
let size =
Algorithm::measure_size(tree, node, known_dimensions, parent_size, available_space, sizing_mode);
SizeAndBaselines { size, first_baselines: Point::NONE }
}
}
}
let display_mode = tree.style(node).display;
let has_children = !tree.is_childless(node);
let computed_size_and_baselines = match (display_mode, has_children) {
(Display::None, _) => perform_computations::<HiddenAlgorithm>(
tree,
node,
known_dimensions,
parent_size,
available_space,
run_mode,
sizing_mode,
),
(Display::Flex, true) => perform_computations::<FlexboxAlgorithm>(
tree,
node,
known_dimensions,
parent_size,
available_space,
run_mode,
sizing_mode,
),
#[cfg(feature = "grid")]
(Display::Grid, true) => perform_computations::<CssGridAlgorithm>(
tree,
node,
known_dimensions,
parent_size,
available_space,
run_mode,
sizing_mode,
),
(_, false) => perform_computations::<LeafAlgorithm>(
tree,
node,
known_dimensions,
parent_size,
available_space,
run_mode,
sizing_mode,
),
};
let cache_slot = compute_cache_slot(known_dimensions, available_space);
*tree.cache_mut(node, cache_slot) = Some(Cache {
known_dimensions,
available_space,
run_mode: cache_run_mode,
cached_size_and_baselines: computed_size_and_baselines,
});
#[cfg(feature = "debug")]
NODE_LOGGER.labelled_debug_log("RESULT", computed_size_and_baselines.size);
#[cfg(any(feature = "debug", feature = "profile"))]
NODE_LOGGER.pop_node();
computed_size_and_baselines
}
#[cfg(feature = "debug")]
fn debug_log_node(
known_dimensions: Size<Option<f32>>,
parent_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
run_mode: RunMode,
sizing_mode: SizingMode,
) {
NODE_LOGGER.debug_log(run_mode);
NODE_LOGGER.labelled_debug_log("sizing_mode", sizing_mode);
NODE_LOGGER.labelled_debug_log("known_dimensions", known_dimensions);
NODE_LOGGER.labelled_debug_log("parent_size", parent_size);
NODE_LOGGER.labelled_debug_log("available_space", available_space);
}
#[inline]
fn compute_cache_slot(known_dimensions: Size<Option<f32>>, available_space: Size<AvailableSpace>) -> usize {
use AvailableSpace::{Definite, MaxContent, MinContent};
let has_known_width = known_dimensions.width.is_some();
let has_known_height = known_dimensions.height.is_some();
if has_known_width && has_known_height {
return 0;
}
if has_known_width && !has_known_height {
return 1 + (available_space.height == MinContent) as usize;
}
if !has_known_width && has_known_height {
return 3 + (available_space.width == MinContent) as usize;
}
match (available_space.width, available_space.height) {
(MaxContent | Definite(_), MaxContent | Definite(_)) => 5,
(MaxContent | Definite(_), MinContent) => 6,
(MinContent, MaxContent | Definite(_)) => 7,
(MinContent, MinContent) => 8,
}
}
#[inline]
fn compute_from_cache(
tree: &mut impl LayoutTree,
node: Node,
known_dimensions: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
run_mode: RunMode,
) -> Option<SizeAndBaselines> {
for idx in 0..CACHE_SIZE {
let entry = tree.cache_mut(node, idx);
if let Some(entry) = entry {
if entry.run_mode == RunMode::ComputeSize && run_mode == RunMode::PeformLayout {
return None;
}
let cached_size = entry.cached_size_and_baselines.size;
if (known_dimensions.width == entry.known_dimensions.width
|| known_dimensions.width == Some(cached_size.width))
&& (known_dimensions.height == entry.known_dimensions.height
|| known_dimensions.height == Some(cached_size.height))
&& (known_dimensions.width.is_some()
|| entry.available_space.width.is_roughly_equal(available_space.width))
&& (known_dimensions.height.is_some()
|| entry.available_space.height.is_roughly_equal(available_space.height))
{
return Some(entry.cached_size_and_baselines);
}
}
}
None
}
struct HiddenAlgorithm;
impl LayoutAlgorithm for HiddenAlgorithm {
const NAME: &'static str = "NONE";
fn perform_layout(
tree: &mut impl LayoutTree,
node: Node,
_known_dimensions: Size<Option<f32>>,
_parent_size: Size<Option<f32>>,
_available_space: Size<AvailableSpace>,
_sizing_mode: SizingMode,
) -> SizeAndBaselines {
perform_hidden_layout(tree, node);
SizeAndBaselines { size: Size::ZERO, first_baselines: Point::NONE }
}
fn measure_size(
_tree: &mut impl LayoutTree,
_node: Node,
_known_dimensions: Size<Option<f32>>,
_parent_size: Size<Option<f32>>,
_available_space: Size<AvailableSpace>,
_sizing_mode: SizingMode,
) -> Size<f32> {
Size::ZERO
}
}
fn perform_hidden_layout(tree: &mut impl LayoutTree, node: Node) {
fn perform_hidden_layout_inner(tree: &mut impl LayoutTree, node: Node, order: u32) {
*tree.layout_mut(node) = Layout::with_order(order);
for i in 0..7 {
*tree.cache_mut(node, i) = None;
}
for order in 0..tree.child_count(node) {
perform_hidden_layout_inner(tree, tree.child(node, order), order as _);
}
}
for order in 0..tree.child_count(node) {
perform_hidden_layout_inner(tree, tree.child(node, order), order as _);
}
}
fn round_layout(tree: &mut Taffy, node_id: Node, cumulative_x: f32, cumulative_y: f32) {
let node = &mut tree.nodes[node_id];
let unrounded_layout = node.unrounded_layout;
let layout = &mut node.final_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);
let child_count = tree.child_count(node_id).unwrap();
for index in 0..child_count {
let child = tree.child(node_id, index);
round_layout(tree, child, cumulative_x, cumulative_y);
}
}
#[cfg(test)]
mod tests {
use super::perform_hidden_layout;
use crate::geometry::{Point, Size};
use crate::style::{Display, Style};
use crate::Taffy;
#[test]
fn hidden_layout_should_hide_recursively() {
let mut taffy = Taffy::new();
let style: Style = Style { display: Display::Flex, size: Size::from_points(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_points(50.0, 50.0), ..Default::default() },
&[child_00, child_01],
)
.unwrap();
perform_hidden_layout(&mut taffy, root);
for (node, _) in taffy.nodes.iter().filter(|(node, _)| *node != root) {
if let Ok(layout) = taffy.layout(node) {
assert_eq!(layout.size, Size::zero());
assert_eq!(layout.location, Point::zero());
}
}
}
}