//! Final data structures that represent the high-level UI layout use crate::geometry::{AbsoluteAxis, Line, Point, Rect, Size}; use crate::style::AvailableSpace; use crate::style_helpers::TaffyMaxContent; use crate::util::sys::{f32_max, f32_min}; /// Whether we are performing a full layout, or we merely need to size the node #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub enum RunMode { /// A full layout for this node and all children should be computed PerformLayout, /// The layout algorithm should be executed such that an accurate container size for the node can be determined. /// Layout steps that aren't necessary for determining the container size of the current node can be skipped. ComputeSize, /// This node should have a null layout set as it has been hidden (i.e. using `Display::None`) PerformHiddenLayout, } /// Whether styles should be taken into account when computing size #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub enum SizingMode { /// Only content contributions should be taken into account ContentSize, /// Inherent size styles should be taken into account in addition to content contributions InherentSize, } /// A set of margins that are available for collapsing with for block layout's margin collapsing #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct CollapsibleMarginSet { /// The largest positive margin positive: f32, /// The smallest negative margin (with largest absolute value) negative: f32, } impl CollapsibleMarginSet { /// A default margin set with no collapsible margins pub const ZERO: Self = Self { positive: 0.0, negative: 0.0 }; /// Create a set from a single margin pub fn from_margin(margin: f32) -> Self { if margin >= 0.0 { Self { positive: margin, negative: 0.0 } } else { Self { positive: 0.0, negative: margin } } } /// Collapse a single margin with this set pub fn collapse_with_margin(mut self, margin: f32) -> Self { if margin >= 0.0 { self.positive = f32_max(self.positive, margin); } else { self.negative = f32_min(self.negative, margin); } self } /// Collapse another margin set with this set pub fn collapse_with_set(mut self, other: CollapsibleMarginSet) -> Self { self.positive = f32_max(self.positive, other.positive); self.negative = f32_min(self.negative, other.negative); self } /// Resolve the resultant margin from this set once all collapsible margins /// have been collapsed into it pub fn resolve(&self) -> f32 { self.positive + self.negative } } /// An axis that layout algorithms can be requested to compute a size for #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub enum RequestedAxis { /// The horizontal axis Horizontal, /// The vertical axis Vertical, /// Both axes Both, } impl From for RequestedAxis { fn from(value: AbsoluteAxis) -> Self { match value { AbsoluteAxis::Horizontal => RequestedAxis::Horizontal, AbsoluteAxis::Vertical => RequestedAxis::Vertical, } } } impl TryFrom for AbsoluteAxis { type Error = (); fn try_from(value: RequestedAxis) -> Result { match value { RequestedAxis::Horizontal => Ok(AbsoluteAxis::Horizontal), RequestedAxis::Vertical => Ok(AbsoluteAxis::Vertical), RequestedAxis::Both => Err(()), } } } /// A struct containing the inputs constraints/hints for laying out a node, which are passed in by the parent #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct LayoutInput { /// Whether we only need to know the Node's size, or whe pub run_mode: RunMode, /// Whether a Node's style sizes should be taken into account or ignored pub sizing_mode: SizingMode, /// Which axis we need the size of pub axis: RequestedAxis, /// Known dimensions represent dimensions (width/height) which should be taken as fixed when performing layout. /// For example, if known_dimensions.width is set to Some(WIDTH) then this means something like: /// /// "What would the height of this node be, assuming the width is WIDTH" /// /// Layout functions will be called with both known_dimensions set for final layout. Where the meaning is: /// /// "The exact size of this node is WIDTHxHEIGHT. Please lay out your children" /// pub known_dimensions: Size>, /// Parent size dimensions are intended to be used for percentage resolution. pub parent_size: Size>, /// Available space represents an amount of space to layout into, and is used as a soft constraint /// for the purpose of wrapping. pub available_space: Size, /// Specific to CSS Block layout. Used for correctly computing margin collapsing. You probably want to set this to `Line::FALSE`. pub vertical_margins_are_collapsible: Line, } impl LayoutInput { /// A LayoutInput that can be used to request hidden layout pub const HIDDEN: LayoutInput = LayoutInput { // The important property for hidden layout run_mode: RunMode::PerformHiddenLayout, // The rest will be ignored known_dimensions: Size::NONE, parent_size: Size::NONE, available_space: Size::MAX_CONTENT, sizing_mode: SizingMode::InherentSize, axis: RequestedAxis::Both, vertical_margins_are_collapsible: Line::FALSE, }; } /// A struct containing the result of laying a single node, which is returned up to the parent node /// /// A baseline is the line on which text sits. Your node likely has a baseline if it is a text node, or contains /// children that may be text nodes. See for details. /// If your node does not have a baseline (or you are unsure how to compute it), then simply return `Point::NONE` /// for the first_baselines field #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct LayoutOutput { /// The size of the node pub size: Size, #[cfg(feature = "content_size")] /// The size of the content within the node pub content_size: Size, /// The first baseline of the node in each dimension, if any pub first_baselines: Point>, /// Top margin that can be collapsed with. This is used for CSS block layout and can be set to /// `CollapsibleMarginSet::ZERO` for other layout modes that don't support margin collapsing pub top_margin: CollapsibleMarginSet, /// Bottom margin that can be collapsed with. This is used for CSS block layout and can be set to /// `CollapsibleMarginSet::ZERO` for other layout modes that don't support margin collapsing pub bottom_margin: CollapsibleMarginSet, /// Whether margins can be collapsed through this node. This is used for CSS block layout and can /// be set to `false` for other layout modes that don't support margin collapsing pub margins_can_collapse_through: bool, } impl LayoutOutput { /// An all-zero `LayoutOutput` for hidden nodes pub const HIDDEN: Self = Self { size: Size::ZERO, #[cfg(feature = "content_size")] content_size: Size::ZERO, first_baselines: Point::NONE, top_margin: CollapsibleMarginSet::ZERO, bottom_margin: CollapsibleMarginSet::ZERO, margins_can_collapse_through: false, }; /// A blank layout output pub const DEFAULT: Self = Self::HIDDEN; /// Constructor to create a `LayoutOutput` from just the size and baselines pub fn from_sizes_and_baselines( size: Size, #[cfg_attr(not(feature = "content_size"), allow(unused_variables))] content_size: Size, first_baselines: Point>, ) -> Self { Self { size, #[cfg(feature = "content_size")] content_size, first_baselines, top_margin: CollapsibleMarginSet::ZERO, bottom_margin: CollapsibleMarginSet::ZERO, margins_can_collapse_through: false, } } /// Construct a `LayoutOutput` from just the container and content sizes pub fn from_sizes(size: Size, content_size: Size) -> Self { Self::from_sizes_and_baselines(size, content_size, Point::NONE) } /// Construct a `LayoutOutput` from just the container's size. pub fn from_outer_size(size: Size) -> Self { Self::from_sizes(size, Size::zero()) } } /// The final result of a layout algorithm for a single node. #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Layout { /// The relative ordering of the node /// /// Nodes with a higher order should be rendered on top of those with a lower order. /// This is effectively a topological sort of each tree. pub order: u32, /// The top-left corner of the node pub location: Point, /// The width and height of the node pub size: Size, #[cfg(feature = "content_size")] /// The width and height of the content inside the node. This may be larger than the size of the node in the case of /// overflowing content and is useful for computing a "scroll width/height" for scrollable nodes pub content_size: Size, /// The size of the scrollbars in each dimension. If there is no scrollbar then the size will be zero. pub scrollbar_size: Size, /// The size of the borders of the node pub border: Rect, /// The size of the padding of the node pub padding: Rect, /// The size of the margin of the node pub margin: Rect, } impl Default for Layout { fn default() -> Self { Self::new() } } impl Layout { /// Creates a new zero-[`Layout`]. /// /// The Zero-layout has size and location set to ZERO. /// The `order` value of this layout is set to the minimum value of 0. /// This means it should be rendered below all other [`Layout`]s. #[must_use] pub const fn new() -> Self { Self { order: 0, location: Point::ZERO, size: Size::zero(), #[cfg(feature = "content_size")] content_size: Size::zero(), scrollbar_size: Size::zero(), border: Rect::zero(), padding: Rect::zero(), margin: Rect::zero(), } } /// Creates a new zero-[`Layout`] with the supplied `order` value. /// /// Nodes with a higher order should be rendered on top of those with a lower order. /// The Zero-layout has size and location set to ZERO. #[must_use] pub const fn with_order(order: u32) -> Self { Self { order, size: Size::zero(), location: Point::ZERO, #[cfg(feature = "content_size")] content_size: Size::zero(), scrollbar_size: Size::zero(), border: Rect::zero(), padding: Rect::zero(), margin: Rect::zero(), } } /// Get the width of the node's content box #[inline] pub fn content_box_width(&self) -> f32 { self.size.width - self.padding.left - self.padding.right - self.border.left - self.border.right } /// Get the height of the node's content box #[inline] pub fn content_box_height(&self) -> f32 { self.size.height - self.padding.top - self.padding.bottom - self.border.top - self.border.bottom } /// Get the size of the node's content box #[inline] pub fn content_box_size(&self) -> Size { Size { width: self.content_box_width(), height: self.content_box_height() } } /// Get x offset of the node's content box relative to it's parent's border box pub fn content_box_x(&self) -> f32 { self.location.x + self.border.left + self.padding.left } /// Get x offset of the node's content box relative to it's parent's border box pub fn content_box_y(&self) -> f32 { self.location.y + self.border.top + self.padding.top } } #[cfg(feature = "content_size")] impl Layout { /// Return the scroll width of the node. /// The scroll width is the difference between the width and the content width, floored at zero pub fn scroll_width(&self) -> f32 { f32_max( 0.0, self.content_size.width + f32_min(self.scrollbar_size.width, self.size.width) - self.size.width + self.border.right, ) } /// Return the scroll height of the node. /// The scroll height is the difference between the height and the content height, floored at zero pub fn scroll_height(&self) -> f32 { f32_max( 0.0, self.content_size.height + f32_min(self.scrollbar_size.height, self.size.height) - self.size.height + self.border.bottom, ) } } /// The additional information from layout algorithm #[cfg(feature = "detailed_layout_info")] #[derive(Debug, Clone, PartialEq)] pub enum DetailedLayoutInfo { /// Enum variant for [`DetailedGridInfo`](crate::compute::grid::DetailedGridInfo) #[cfg(feature = "grid")] Grid(Box), /// For node that hasn't had any detailed information yet None, }