diff --git a/src/compute/block.rs b/src/compute/block.rs
index 35f5be89f..e16b53eef 100644
--- a/src/compute/block.rs
+++ b/src/compute/block.rs
@@ -11,6 +11,30 @@ use crate::util::MaybeMath;
use crate::util::{MaybeResolve, ResolveOrZero};
use crate::{BlockContainerStyle, BlockItemStyle, BoxGenerationMode, BoxSizing, LayoutBlockContainer, TextAlign};
+#[cfg(feature = "float_layout")]
+use super::{FloatContext, FloatDirection, FloatedBox};
+
+/// Context for positioning Block and Float boxes within a Block Formatting Context
+pub struct BlockFormattingContext {
+ #[cfg(feature = "float_layout")]
+ float_context: super::FloatContext,
+}
+
+impl BlockFormattingContext {
+ /// Create a new `BlockFormattingContext` with the specified width constraint
+ pub fn new(available_space: AvailableSpace) -> Self {
+ Self {
+ #[cfg(feature = "float_layout")]
+ float_context: FloatContext::new(available_space),
+ }
+ }
+
+ #[cfg(feature = "float_layout")]
+ pub fn place_floated_box(&mut self, floated_box: FloatedBox, float_direction: FloatDirection) -> Point<f32> {
+ self.float_context.place_floated_box(floated_box, float_direction)
+ }
+}
+
#[cfg(feature = "content_size")]
use super::common::content_size::compute_content_size_contribution;
@@ -65,6 +89,7 @@ pub fn compute_block_layout(
tree: &mut impl LayoutBlockContainer,
node_id: NodeId,
inputs: LayoutInput,
+ bfc: Option<&mut BlockFormattingContext>,
) -> LayoutOutput {
let LayoutInput { known_dimensions, parent_size, run_mode, .. } = inputs;
let style = tree.get_block_container_style(node_id);
@@ -117,12 +142,28 @@ pub fn compute_block_layout(
}
}
+ // Unwrap the block formatting context if one was passed, or else create a new one
+ let mut new_bfc: BlockFormattingContext;
+ let bfc_ref: &mut BlockFormattingContext;
+ match bfc {
+ Some(inherited_bfc) => bfc_ref = inherited_bfc,
+ None => {
+ new_bfc = BlockFormattingContext::new(inputs.available_space.width);
+ bfc_ref = &mut new_bfc;
+ }
+ }
+
debug_log!("BLOCK");
- compute_inner(tree, node_id, LayoutInput { known_dimensions: styled_based_known_dimensions, ..inputs })
+ compute_inner(tree, node_id, LayoutInput { known_dimensions: styled_based_known_dimensions, ..inputs }, bfc_ref)
}
/// Computes the layout of [`LayoutBlockContainer`] according to the block layout algorithm
-fn compute_inner(tree: &mut impl LayoutBlockContainer, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput {
+fn compute_inner(
+ tree: &mut impl LayoutBlockContainer,
+ node_id: NodeId,
+ inputs: LayoutInput,
+ bfc: &mut BlockFormattingContext,
+) -> LayoutOutput {
let LayoutInput {
known_dimensions, parent_size, available_space, run_mode, vertical_margins_are_collapsible, ..
} = inputs;
diff --git a/src/compute/float.rs b/src/compute/float.rs
index 9fac7d16c..d53455696 100644
--- a/src/compute/float.rs
+++ b/src/compute/float.rs
@@ -26,11 +26,12 @@
//!
//! <https://www.w3.org/TR/CSS22/visuren.html#floats>
-use crate::AvailableSpace;
+use crate::{AvailableSpace, Point};
/// A context for placing floated boxes
+#[derive(Debug, Clone)]
pub struct FloatContext {
- available_width: f32,
+ available_space: AvailableSpace,
/// A list of left-floated boxes within the context
left_floats: Vec<PlacedFloatedBox>,
/// A list of right-floated boxes within the context
@@ -40,13 +41,14 @@ pub struct FloatContext {
segments: Vec<Segment>,
}
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FloatDirection {
Left,
Right,
}
/// A floated box to place within the context
-#[derive(Default)]
+#[derive(Debug, Clone, Default)]
pub struct FloatedBox {
/// A user defined ID for the box
id: u64,
@@ -55,19 +57,23 @@ pub struct FloatedBox {
}
/// A floated box
-#[derive(Default)]
+#[derive(Debug, Clone, Default)]
struct PlacedFloatedBox {
+ /// A user defined ID for the box
+ id: u64,
+ width: f32,
+ height: f32,
/// Distance from the edge of the container that the box is floated towards
/// (distance from the left for left floats, from the right for right floats)
x_inset: f32,
y: f32,
- inner: FloatedBox,
}
impl PlacedFloatedBox {
- const DEFAULT: Self = Self { x_inset: 0.0, y: 0.0, inner: FloatedBox { id: 0, width: 0.0, height: 0.0 } };
+ const DEFAULT: Self = Self { id: 0, width: 0.0, height: 0.0, x_inset: 0.0, y: 0.0 };
}
+#[derive(Debug, Clone)]
struct Segment {
y_start: f32,
y_end: f32,
@@ -77,18 +83,57 @@ struct Segment {
impl FloatContext {
/// Create a new empty `FloatContext`
- pub fn new(available_width: f32) -> Self {
- Self { available_width, left_floats: Vec::new(), right_floats: Vec::new(), segments: Vec::new() }
+ pub fn new(available_space: AvailableSpace) -> Self {
+ Self { available_space, left_floats: Vec::new(), right_floats: Vec::new(), segments: Vec::new() }
}
/// Position a floated box with the context
- pub fn place_box(&mut self, floated_box: FloatedBox, float_direction: FloatDirection) {
+ pub fn place_floated_box(&mut self, floated_box: FloatedBox, float_direction: FloatDirection) -> Point<f32> {
let last_float = self.get_float_list(float_direction).last().unwrap_or(&PlacedFloatedBox::DEFAULT);
- let y_threshold = if self.available_width - last_float.inner.width < 0.0 {
- last_float.y + last_float.inner.height
- } else {
- last_float.y
+ let (x_inset, y): (f32, f32) = match self.available_space {
+ AvailableSpace::MinContent => {
+ let x_inset = 0.0;
+ let y = last_float.y + last_float.height;
+ (x_inset, y)
+ }
+ AvailableSpace::MaxContent => {
+ let x_inset = last_float.x_inset + last_float.width;
+ let y = last_float.y;
+ (x_inset, y)
+ }
+ AvailableSpace::Definite(available_width) => {
+ let line_available_width = available_width - last_float.x_inset - last_float.width;
+
+ if line_available_width < floated_box.width {
+ let x_inset = 0.0;
+ let y = last_float.y + last_float.height;
+ (x_inset, y)
+ } else {
+ let x_inset = last_float.x_inset + last_float.width;
+ let y = last_float.y;
+ (x_inset, y)
+ }
+ }
+ };
+
+ let placed_floated_box =
+ PlacedFloatedBox { id: floated_box.id, width: floated_box.width, height: floated_box.height, x_inset, y };
+
+ match float_direction {
+ FloatDirection::Left => self.left_floats.push(placed_floated_box),
+ FloatDirection::Right => self.right_floats.push(placed_floated_box),
+ }
+
+ // Return the (x, y) coordinates of the positioned box
+ return match self.available_space {
+ // Position won't actually be used if we're layouting under a min-content
+ // or max-content constraint, so just return (0, 0)
+ AvailableSpace::MinContent | AvailableSpace::MaxContent => Point::ZERO,
+ AvailableSpace::Definite(width) => match float_direction {
+ FloatDirection::Left => Point { x: x_inset, y },
+ FloatDirection::Right => Point { x: width - x_inset, y },
+ },
};
}
diff --git a/src/compute/mod.rs b/src/compute/mod.rs
index 45ea25698..fbe9d491e 100644
--- a/src/compute/mod.rs
+++ b/src/compute/mod.rs
@@ -39,7 +39,7 @@ pub(crate) mod grid;
pub use leaf::compute_leaf_layout;
#[cfg(feature = "block_layout")]
-pub use self::block::compute_block_layout;
+pub use self::block::{compute_block_layout, BlockFormattingContext};
#[cfg(feature = "flexbox")]
pub use self::flexbox::compute_flexbox_layout;
@@ -47,6 +47,8 @@ pub use self::flexbox::compute_flexbox_layout;
#[cfg(feature = "grid")]
pub use self::grid::compute_grid_layout;
+#[cfg(feature = "float_layout")]
+use self::float::FloatDirection;
#[cfg(feature = "float_layout")]
pub use self::float::{FloatContext, FloatedBox};
@@ -168,10 +170,10 @@ pub fn compute_cached_layout<Tree: CacheTree + ?Sized, ComputeFunction>(
tree: &mut Tree,
node: NodeId,
inputs: LayoutInput,
- mut compute_uncached: ComputeFunction,
+ compute_uncached: ComputeFunction,
) -> LayoutOutput
where
- ComputeFunction: FnMut(&mut Tree, NodeId, LayoutInput) -> LayoutOutput,
+ ComputeFunction: FnOnce(&mut Tree, NodeId, LayoutInput) -> LayoutOutput,
{
debug_push_node!(node);
let LayoutInput { known_dimensions, available_space, run_mode, .. } = inputs;
diff --git a/src/lib.rs b/src/lib.rs
index 7cd927918..da21ed14d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -123,6 +123,7 @@ pub use crate::tree::TaffyTree;
#[doc(inline)]
pub use crate::util::print_tree;
+pub use crate::compute::*;
pub use crate::geometry::*;
pub use crate::style::*;
pub use crate::tree::*;
diff --git a/src/tree/taffy_tree.rs b/src/tree/taffy_tree.rs
index af343aebf..e7c8e0097 100644
--- a/src/tree/taffy_tree.rs
+++ b/src/tree/taffy_tree.rs
@@ -18,7 +18,9 @@ use crate::util::sys::{new_vec_with_capacity, ChildrenVec, Vec};
use crate::compute::{
compute_cached_layout, compute_hidden_layout, compute_leaf_layout, compute_root_layout, round_layout,
};
+use crate::BlockFormattingContext;
use crate::CacheTree;
+
#[cfg(feature = "block_layout")]
use crate::{compute::compute_block_layout, LayoutBlockContainer};
#[cfg(feature = "flexbox")]
@@ -283,6 +285,67 @@ where
pub(crate) measure_function: MeasureFunction,
}
+impl<NodeContext, MeasureFunction> TaffyView<'_, NodeContext, MeasureFunction>
+where
+ MeasureFunction:
+ FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
+{
+ #[inline(always)]
+ fn compute_child_layout(
+ &mut self,
+ node_id: NodeId,
+ inputs: LayoutInput,
+ #[cfg(feature = "block_layout")] bfc: Option<&mut BlockFormattingContext>,
+ ) -> LayoutOutput {
+ // If RunMode is PerformHiddenLayout then this indicates that an ancestor node is `Display::None`
+ // and thus that we should lay out this node using hidden layout regardless of it's own display style.
+ if inputs.run_mode == RunMode::PerformHiddenLayout {
+ debug_log!("HIDDEN");
+ return compute_hidden_layout(self, node_id);
+ }
+
+ // We run the following wrapped in "compute_cached_layout", which will check the cache for an entry matching the node and inputs and:
+ // - Return that entry if exists
+ // - Else call the passed closure (below) to compute the result
+ //
+ // If there was no cache match and a new result needs to be computed then that result will be added to the cache
+ compute_cached_layout(self, node_id, inputs, |tree, node_id, inputs| {
+ let display_mode = tree.taffy.nodes[node_id.into()].style.display;
+ let has_children = tree.child_count(node_id) > 0;
+
+ debug_log!(display_mode);
+ debug_log_node!(
+ inputs.known_dimensions,
+ inputs.parent_size,
+ inputs.available_space,
+ inputs.run_mode,
+ inputs.sizing_mode
+ );
+
+ // Dispatch to a layout algorithm based on the node's display style and whether the node has children or not.
+ match (display_mode, has_children) {
+ (Display::None, _) => compute_hidden_layout(tree, node_id),
+ #[cfg(feature = "block_layout")]
+ (Display::Block, true) => compute_block_layout(tree, node_id, inputs, bfc),
+ #[cfg(feature = "flexbox")]
+ (Display::Flex, true) => compute_flexbox_layout(tree, node_id, inputs),
+ #[cfg(feature = "grid")]
+ (Display::Grid, true) => compute_grid_layout(tree, node_id, inputs),
+ (_, false) => {
+ let node_key = node_id.into();
+ let style = &tree.taffy.nodes[node_key].style;
+ let has_context = tree.taffy.nodes[node_key].has_context;
+ let node_context = has_context.then(|| tree.taffy.node_context_data.get_mut(node_key)).flatten();
+ let measure_function = |known_dimensions, available_space| {
+ (tree.measure_function)(known_dimensions, available_space, node_id, node_context, style)
+ };
+ compute_leaf_layout(inputs, style, |_, _| 0.0, measure_function)
+ }
+ }
+ })
+ }
+}
+
// TraversePartialTree impl for TaffyView
impl<NodeContext, MeasureFunction> TraversePartialTree for TaffyView<'_, NodeContext, MeasureFunction>
where
@@ -346,54 +409,8 @@ where
}
#[inline(always)]
- fn compute_child_layout(&mut self, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
- // If RunMode is PerformHiddenLayout then this indicates that an ancestor node is `Display::None`
- // and thus that we should lay out this node using hidden layout regardless of it's own display style.
- if inputs.run_mode == RunMode::PerformHiddenLayout {
- debug_log!("HIDDEN");
- return compute_hidden_layout(self, node);
- }
-
- // We run the following wrapped in "compute_cached_layout", which will check the cache for an entry matching the node and inputs and:
- // - Return that entry if exists
- // - Else call the passed closure (below) to compute the result
- //
- // If there was no cache match and a new result needs to be computed then that result will be added to the cache
- compute_cached_layout(self, node, inputs, |tree, node, inputs| {
- let display_mode = tree.taffy.nodes[node.into()].style.display;
- let has_children = tree.child_count(node) > 0;
-
- debug_log!(display_mode);
- debug_log_node!(
- inputs.known_dimensions,
- inputs.parent_size,
- inputs.available_space,
- inputs.run_mode,
- inputs.sizing_mode
- );
-
- // Dispatch to a layout algorithm based on the node's display style and whether the node has children or not.
- match (display_mode, has_children) {
- (Display::None, _) => compute_hidden_layout(tree, node),
- #[cfg(feature = "block_layout")]
- (Display::Block, true) => compute_block_layout(tree, node, inputs),
- #[cfg(feature = "flexbox")]
- (Display::Flex, true) => compute_flexbox_layout(tree, node, inputs),
- #[cfg(feature = "grid")]
- (Display::Grid, true) => compute_grid_layout(tree, node, inputs),
- (_, false) => {
- let node_key = node.into();
- let style = &tree.taffy.nodes[node_key].style;
- let has_context = tree.taffy.nodes[node_key].has_context;
- let node_context = has_context.then(|| tree.taffy.node_context_data.get_mut(node_key)).flatten();
- let measure_function = |known_dimensions, available_space| {
- (tree.measure_function)(known_dimensions, available_space, node, node_context, style)
- };
- // TODO: implement calc() in high-level API
- compute_leaf_layout(inputs, style, |_, _| 0.0, measure_function)
- }
- }
- })
+ fn compute_child_layout(&mut self, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput {
+ self.compute_child_layout(node_id, inputs, None)
}
}
@@ -452,6 +469,16 @@ where
fn get_block_child_style(&self, child_node_id: NodeId) -> Self::BlockItemStyle<'_> {
self.get_core_container_style(child_node_id)
}
+
+ #[inline(always)]
+ fn compute_block_child_layout(
+ &mut self,
+ node_id: NodeId,
+ inputs: LayoutInput,
+ bfc: Option<&mut BlockFormattingContext>,
+ ) -> LayoutOutput {
+ self.compute_child_layout(node_id, inputs, bfc)
+ }
}
#[cfg(feature = "flexbox")]
diff --git a/src/tree/traits.rs b/src/tree/traits.rs
index d90d1e9e0..52c1a2a0f 100644
--- a/src/tree/traits.rs
+++ b/src/tree/traits.rs
@@ -137,7 +137,7 @@ use crate::style::{FlexboxContainerStyle, FlexboxItemStyle};
use crate::style::{GridContainerStyle, GridItemStyle};
use crate::CheapCloneStr;
#[cfg(feature = "block_layout")]
-use crate::{BlockContainerStyle, BlockItemStyle};
+use crate::{BlockContainerStyle, BlockFormattingContext, BlockItemStyle};
#[cfg(all(feature = "grid", feature = "detailed_layout_info"))]
use crate::compute::grid::DetailedGridInfo;
@@ -313,6 +313,18 @@ pub trait LayoutBlockContainer: LayoutPartialTree {
/// Get the child's styles
fn get_block_child_style(&self, child_node_id: NodeId) -> Self::BlockItemStyle<'_>;
+
+ /// Compute the specified node's size or full layout given the specified constraints
+ #[cfg(feature = "block_layout")]
+ fn compute_block_child_layout(
+ &mut self,
+ node_id: NodeId,
+ inputs: LayoutInput,
+ bfc: Option<&mut BlockFormattingContext>,
+ ) -> LayoutOutput {
+ let _ = bfc;
+ self.compute_child_layout(node_id, inputs)
+ }
}
// --- PRIVATE TRAITS