diff --git a/RELEASES.md b/RELEASES.md index d8d05efe5..1ed682bf0 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -10,7 +10,7 @@ - removed the public `Number` type; a more idiomatic `Option` is used instead - the associated public `MinMax` and `OrElse` traits have also been removed; these should never have been public -- `Sprawl::remove` now returns a `Result`, to indicate if the operation was sucessful, and if it was, which ID was invalidated. +- `Sprawl::remove` now returns a `Result`, to indicate if the operation was sucessful, and if it was, which ID was invalidated. - renamed `taffy::forest::Forest.new-node(..)` `taffy::forest::Forest.new_with_children(..)` - renamed `taffy::node::Taffy.new-node(..)` -> `taffy::node::Taffy.new_with_children(..)` - renamed `taffy::style::Style` -> `taffy::style::FlexboxLayout` to more precicely indicate its purpose diff --git a/src/flexbox.rs b/src/flexbox.rs index 1314996fa..7cb9fd450 100644 --- a/src/flexbox.rs +++ b/src/flexbox.rs @@ -18,14 +18,14 @@ struct FlexItem { node: NodeId, /// The base size of this item - size: Size>, + size: Size, /// The minimum allowable size of this item - min_size: Size>, + min_size: Size, /// The maximum allowable size of this item - max_size: Size>, + max_size: Size, /// The final offset of this item - position: Rect>, + position: Rect, /// The margin of this item margin: Rect, /// The padding of this item @@ -95,7 +95,7 @@ struct AlgoConstants { padding_border: Rect, /// The size of the internal node - node_inner_size: Size>, + node_inner_size: Size, /// The size of the surrounding container container_size: Size, /// The size of the internal container @@ -104,14 +104,10 @@ struct AlgoConstants { impl Forest { /// Computes the layout of this [`Forest`] according to the flexbox algorithm - pub(crate) fn compute(&mut self, root: NodeId, size: Size>) { + pub(crate) fn compute(&mut self, root: NodeId, size: Size) { let style = self.nodes[root].style; - let has_root_min_max = style.min_size.width.is_defined() - || style.min_size.height.is_defined() - || style.max_size.width.is_defined() - || style.max_size.height.is_defined(); - let preliminary_size = if has_root_min_max { + let preliminary_size = if style.has_defined_size() { let first_pass = self.compute_preliminary(root, style.size.resolve(size), size, false, true); self.compute_preliminary( @@ -119,13 +115,13 @@ impl Forest { Size { width: first_pass .width - .maybe_max(style.min_size.width.resolve(size.width)) - .maybe_min(style.max_size.width.resolve(size.width)) + .maybe_max(style.min_size.width.and_then(|max_width| max_width.resolve(size.width))) + .maybe_min(style.max_size.width.and_then(|min_width| min_width.resolve(size.width))) .into(), height: first_pass .height - .maybe_max(style.min_size.height.resolve(size.height)) - .maybe_min(style.max_size.height.resolve(size.height)) + .maybe_max(style.min_size.height.and_then(|max_height| max_height.resolve(size.height))) + .maybe_min(style.max_size.height.and_then(|min_height| min_height.resolve(size.height))) .into(), }, size, @@ -150,8 +146,8 @@ impl Forest { layout.location.x = round(layout.location.x); layout.location.y = round(layout.location.y); - layout.size.width = round(layout.size.width); - layout.size.height = round(layout.size.height); + layout.size.width = layout.size.width.map(|width| width.round()); + layout.size.height = layout.size.height.map(|height| height.round()); for child in &children[root] { Self::round_layout(nodes, children, *child, abs_x, abs_y); @@ -172,21 +168,22 @@ impl Forest { fn compute_from_cache( &mut self, node: NodeId, - node_size: Size>, - parent_size: Size>, + node_size: Size, + parent_size: Size, perform_layout: bool, main_size: bool, ) -> Option> { if let Some(ref cache) = self.cache(node, main_size) { if cache.perform_layout || !perform_layout { let width_compatible = if let Some(width) = node_size.width { - abs(width - cache.size.width) < f32::EPSILON + abs(width.maybe_sub(cache.size.width)) < f32::EPSILON } else { cache.node_size.width.is_none() }; let height_compatible = if let Some(height) = node_size.height { - abs(height - cache.size.height) < f32::EPSILON + // More maybemath shenanigans + abs(height.maybe_sub(cache.size.height)) < f32::EPSILON } else { cache.node_size.height.is_none() }; @@ -206,31 +203,29 @@ impl Forest { /// Compute constants that can be reused during the flexbox algorithm. #[inline] - fn compute_constants( - &self, - node: NodeId, - node_size: Size>, - parent_size: Size>, - ) -> AlgoConstants { + fn compute_constants(&self, node: NodeId, node_size: Size, parent_size: Size) -> AlgoConstants { let dir = self.nodes[node].style.flex_direction; let is_row = dir.is_row(); let is_column = dir.is_column(); let is_wrap_reverse = self.nodes[node].style.flex_wrap == FlexWrap::WrapReverse; - let margin = self.nodes[node].style.margin.map(|n| n.resolve(parent_size.width).unwrap_or(0.0)); - let padding = self.nodes[node].style.padding.map(|n| n.resolve(parent_size.width).unwrap_or(0.0)); - let border = self.nodes[node].style.border.map(|n| n.resolve(parent_size.width).unwrap_or(0.0)); + let margin = + self.nodes[node].style.margin.map(|n| n.map(|d| d.resolve(parent_size.width)).unwrap_or(Some(0.0))); + let padding = + self.nodes[node].style.padding.map(|n| n.map(|d| d.resolve(parent_size.width)).unwrap_or(Some(0.0))); + let border = + self.nodes[node].style.border.map(|n| n.map(|d| d.resolve(parent_size.width)).unwrap_or(Some(0.0))); let padding_border = Rect { - start: padding.start + border.start, - end: padding.end + border.end, - top: padding.top + border.top, - bottom: padding.bottom + border.bottom, + start: padding.start.maybe_add(border.start), + end: padding.end.maybe_add(border.end), + top: padding.top.maybe_add(border.top), + bottom: padding.bottom.maybe_add(border.bottom), }; - let node_inner_size = Size { - width: node_size.width.maybe_sub(padding_border.horizontal_axis_sum()), - height: node_size.height.maybe_sub(padding_border.vertical_axis_sum()), + let node_inner_size: Size = Size { + width: node_size.width.and_then(|w| w - padding_border.horizontal_axis_sum()), + height: Some(node_size.height.maybe_sub(padding_border.vertical_axis_sum())), }; let container_size = Size::zero(); @@ -302,11 +297,7 @@ impl Forest { /// **This might result in an infinite value**. #[inline] #[must_use] - fn determine_available_space( - node_size: Size>, - parent_size: Size>, - constants: &AlgoConstants, - ) -> Size> { + fn determine_available_space(node_size: Size, parent_size: Size, constants: &AlgoConstants) -> Size { let width = match node_size.width { Some(node_width) => Some(node_width), None => parent_size @@ -357,9 +348,9 @@ impl Forest { fn determine_flex_base_size( &mut self, node: NodeId, - node_size: Size>, + node_size: Size, constants: &AlgoConstants, - available_space: Size>, + available_space: Size, flex_items: &mut Vec, ) { // TODO - this does not follow spec. See the TODOs below @@ -441,7 +432,8 @@ impl Forest { true, ) .main(constants.dir) - .maybe_min(child.max_size.main(constants.dir)); + .maybe_min(child.max_size.main(constants.dir)) + .unwrap(); // TODO: This is probably not correct } // The hypothetical main size is the item’s flex base size clamped according to its @@ -465,7 +457,7 @@ impl Forest { child.hypothetical_inner_size.set_main( constants.dir, - child.flex_basis.maybe_max(min_main).maybe_min(child.max_size.main(constants.dir)), + Some(child.flex_basis.maybe_max(min_main).maybe_min(child.max_size.main(constants.dir))), // Will this lead to Some(None) outputs? If so, how to unrawp correctly? ); child.hypothetical_outer_size.set_main( @@ -497,7 +489,7 @@ impl Forest { &self, node: NodeId, constants: &AlgoConstants, - available_space: Size>, + available_space: Size, flex_items: &'a mut Vec, ) -> Vec> { let mut lines = crate::sys::new_vec_with_capacity(1); @@ -513,7 +505,7 @@ impl Forest { .iter() .enumerate() .find(|&(idx, child)| { - line_length += child.hypothetical_outer_size.main(constants.dir); + line_length = line_length.maybe_add(child.hypothetical_outer_size.main(constants.dir)); if let Some(main) = available_space.main(constants.dir) { line_length > main && idx != 0 } else { @@ -536,12 +528,7 @@ impl Forest { /// /// # [9.7. Resolving Flexible Lengths](https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) #[inline] - fn resolve_flexible_lengths( - &mut self, - line: &mut FlexLine, - constants: &AlgoConstants, - available_space: Size>, - ) { + fn resolve_flexible_lengths(&mut self, line: &mut FlexLine, constants: &AlgoConstants, available_space: Size) { // 1. Determine the used flex factor. Sum the outer hypothetical main sizes of all // items on the line. If the sum is less than the flex container’s inner main size, // use the flex grow factor for the rest of this algorithm; otherwise, use the @@ -682,7 +669,10 @@ impl Forest { for child in &mut unfrozen { child.target_size.set_main( constants.dir, - child.flex_basis + free_space * (self.nodes[child.node].style.flex_grow / sum_flex_grow), + Some( + child.flex_basis + + free_space * (self.nodes[child.node].style.flex_grow / sum_flex_grow), + ), ); } } else if shrinking && sum_flex_shrink > 0.0 { @@ -697,7 +687,7 @@ impl Forest { child.inner_flex_basis * self.nodes[child.node].style.flex_shrink; child.target_size.set_main( constants.dir, - child.flex_basis + free_space * (scaled_shrink_factor / sum_scaled_shrink_factor), + Some(child.flex_basis + free_space * (scaled_shrink_factor / sum_scaled_shrink_factor)), ) } } @@ -769,7 +759,7 @@ impl Forest { &mut self, line: &mut FlexLine, constants: &AlgoConstants, - available_space: Size>, + available_space: Size, ) { for child in line.items.iter_mut() { let child_cross = child @@ -818,7 +808,7 @@ impl Forest { fn calculate_children_base_lines( &mut self, node: NodeId, - node_size: Size>, + node_size: Size, flex_lines: &mut [FlexLine], constants: &AlgoConstants, ) { @@ -898,7 +888,7 @@ impl Forest { &mut self, flex_lines: &mut [FlexLine], node: NodeId, - node_size: Size>, + node_size: Size, constants: &AlgoConstants, ) { if flex_lines.len() == 1 && node_size.cross(constants.dir).is_some() { @@ -926,13 +916,13 @@ impl Forest { .map(|child| { let child_style = &self.nodes[child.node].style; if child_style.align_self(&self.nodes[node].style) == AlignSelf::Baseline - && child_style.cross_margin_start(constants.dir) != Dimension::Auto - && child_style.cross_margin_end(constants.dir) != Dimension::Auto - && child_style.cross_size(constants.dir) == Dimension::Auto + && child_style.cross_margin_start(constants.dir) != Some(Dimension::Auto) + && child_style.cross_margin_end(constants.dir) != Some(Dimension::Auto) + && child_style.cross_size(constants.dir) == Some(Dimension::Auto) { - max_baseline - child.baseline + child.hypothetical_outer_size.cross(constants.dir) + max_baseline - child.baseline.maybe_add(child.hypothetical_outer_size.cross(constants.dir)) } else { - child.hypothetical_outer_size.cross(constants.dir) + child.hypothetical_outer_size.cross(constants.dir).unwrap() } }) .fold(0.0, |acc, x| acc.max(x)); @@ -952,7 +942,7 @@ impl Forest { &mut self, flex_lines: &mut [FlexLine], node: NodeId, - node_size: Size>, + node_size: Size, constants: &AlgoConstants, ) { if self.nodes[node].style.align_content == AlignContent::Stretch && node_size.cross(constants.dir).is_some() { @@ -990,9 +980,9 @@ impl Forest { child.target_size.set_cross( constants.dir, if child_style.align_self(&self.nodes[node].style) == AlignSelf::Stretch - && child_style.cross_margin_start(constants.dir) != Dimension::Auto - && child_style.cross_margin_end(constants.dir) != Dimension::Auto - && child_style.cross_size(constants.dir) == Dimension::Auto + && child_style.cross_margin_start(constants.dir) != Some(Dimension::Auto) + && child_style.cross_margin_end(constants.dir) != Some(Dimension::Auto) + && child_style.cross_size(constants.dir) == Some(Dimension::Auto) { (line_cross_size - child.margin.cross_axis_sum(constants.dir)) .maybe_max(child.min_size.cross(constants.dir)) @@ -1034,10 +1024,10 @@ impl Forest { for child in line.items.iter_mut() { let child_style = &self.nodes[child.node].style; - if child_style.main_margin_start(constants.dir) == Dimension::Auto { + if child_style.main_margin_start(constants.dir) == Some(Dimension::Auto) { num_auto_margins += 1; } - if child_style.main_margin_end(constants.dir) == Dimension::Auto { + if child_style.main_margin_end(constants.dir) == Some(Dimension::Auto) { num_auto_margins += 1; } } @@ -1047,14 +1037,14 @@ impl Forest { for child in line.items.iter_mut() { let child_style = &self.nodes[child.node].style; - if child_style.main_margin_start(constants.dir) == Dimension::Auto { + if child_style.main_margin_start(constants.dir) == Some(Dimension::Auto) { if constants.is_row { child.margin.start = margin; } else { child.margin.top = margin; } } - if child_style.main_margin_end(constants.dir) == Dimension::Auto { + if child_style.main_margin_end(constants.dir) == Some(Dimension::Auto) { if constants.is_row { child.margin.end = margin; } else { @@ -1142,30 +1132,30 @@ impl Forest { let max_baseline: f32 = line.items.iter_mut().map(|child| child.baseline).fold(0.0, |acc, x| acc.max(x)); for child in line.items.iter_mut() { - let free_space = line_cross_size - child.outer_target_size.cross(constants.dir); + let free_space = line_cross_size.maybe_sub(child.outer_target_size.cross(constants.dir)); let child_style = &self.nodes[child.node].style; - if child_style.cross_margin_start(constants.dir) == Dimension::Auto - && child_style.cross_margin_end(constants.dir) == Dimension::Auto + if child_style.cross_margin_start(constants.dir) == Some(Dimension::Auto) + && child_style.cross_margin_end(constants.dir) == Some(Dimension::Auto) { if constants.is_row { - child.margin.top = free_space / 2.0; - child.margin.bottom = free_space / 2.0; + child.margin.top = Some(free_space / 2.0); + child.margin.bottom = Some(free_space / 2.0); } else { - child.margin.start = free_space / 2.0; - child.margin.end = free_space / 2.0; + child.margin.start = Some(free_space / 2.0); + child.margin.end = Some(free_space / 2.0); } - } else if child_style.cross_margin_start(constants.dir) == Dimension::Auto { + } else if child_style.cross_margin_start(constants.dir) == Some(Dimension::Auto) { if constants.is_row { - child.margin.top = free_space; + child.margin.top = Some(free_space); } else { - child.margin.start = free_space; + child.margin.start = Some(free_space); } - } else if child_style.cross_margin_end(constants.dir) == Dimension::Auto { + } else if child_style.cross_margin_end(constants.dir) == Some(Dimension::Auto) { if constants.is_row { - child.margin.bottom = free_space; + child.margin.bottom = Some(free_space); } else { - child.margin.end = free_space; + child.margin.end = Some(free_space); } } else { // 14. Align all flex items along the cross-axis. @@ -1251,16 +1241,18 @@ impl Forest { #[must_use] fn determine_container_cross_size( flex_lines: &mut [FlexLine], - node_size: Size>, + node_size: Size, constants: &mut AlgoConstants, ) -> f32 { let total_cross_size: f32 = flex_lines.iter().map(|line| line.cross_size).sum(); constants.container_size.set_cross( constants.dir, - node_size - .cross(constants.dir) - .unwrap_or(total_cross_size + constants.padding_border.cross_axis_sum(constants.dir)), + Some( + node_size + .cross(constants.dir) + .unwrap_or(total_cross_size + constants.padding_border.cross_axis_sum(constants.dir)), + ), ); constants.inner_container_size.set_cross( @@ -1356,17 +1348,27 @@ impl Forest { ); let offset_main = total_offset_main - + child.offset_main - + child.margin.main_start(constants.dir) - + (child.position.main_start(constants.dir).unwrap_or(0.0) - - child.position.main_end(constants.dir).unwrap_or(0.0)); + .maybe_add(child.offset_main) + .maybe_add(child.margin.main_start(constants.dir)) + .maybe_add( + child + .position + .main_start(constants.dir) + .unwrap_or(0.0) + .maybe_sub(child.position.main_end(constants.dir)), + ); let offset_cross = total_offset_cross - + child.offset_cross - + line_offset_cross - + child.margin.cross_start(constants.dir) - + (child.position.cross_start(constants.dir).unwrap_or(0.0) - - child.position.cross_end(constants.dir).unwrap_or(0.0)); + .maybe_add(child.offset_cross) + .maybe_add(line_offset_cross) + .maybe_add(child.margin.cross_start(constants.dir)) + .maybe_add( + child + .position + .cross_start(constants.dir) + .unwrap_or(0.0) + .maybe_sub(child.position.cross_end(constants.dir)), + ); self.nodes[child.node].layout = Layout { order: self.children[node].iter().position(|n| *n == child.node).unwrap() as u32, @@ -1554,8 +1556,8 @@ impl Forest { fn compute_preliminary( &mut self, node: NodeId, - node_size: Size>, - parent_size: Size>, + node_size: Size, + parent_size: Size, perform_layout: bool, main_size: bool, ) -> Size { @@ -1572,7 +1574,7 @@ impl Forest { // If this is a leaf node we can skip a lot of this function in some cases if self.children[node].is_empty() { if node_size.width.is_some() && node_size.height.is_some() { - return node_size.map(|s| s.unwrap_or(0.0)); + return node_size.map(|s| Some(s.unwrap_or(0.0))); } if let Some(ref measure) = self.nodes[node].measure { @@ -1587,8 +1589,8 @@ impl Forest { } return Size { - width: node_size.width.unwrap_or(0.0) + constants.padding_border.horizontal_axis_sum(), - height: node_size.height.unwrap_or(0.0) + constants.padding_border.vertical_axis_sum(), + width: node_size.width.maybe_add(constants.padding_border.horizontal_axis_sum()), + height: Some(node_size.height.unwrap_or(0.0) + constants.padding_border.vertical_axis_sum()), }; } @@ -1626,7 +1628,7 @@ impl Forest { // Not part of the spec from what i can see but seems correct constants.container_size.set_main( constants.dir, - node_size.main(constants.dir).unwrap_or({ + Some(node_size.main(constants.dir).unwrap_or({ let longest_line = flex_lines.iter().fold(f32::MIN, |acc, line| { let length: f32 = line.items.iter().map(|item| item.outer_target_size.main(constants.dir)).sum(); acc.max(length) @@ -1637,7 +1639,7 @@ impl Forest { Some(val) if flex_lines.len() > 1 && size < val => val, _ => size, } - }), + })), ); constants.inner_container_size.set_main( diff --git a/src/forest.rs b/src/forest.rs index 8f7b0d095..b59abcf70 100644 --- a/src/forest.rs +++ b/src/forest.rs @@ -13,7 +13,7 @@ use crate::sys::{new_vec_with_capacity, ChildrenVec, ParentsVec, Vec}; pub(crate) struct NodeData { /// The layout strategy used by this node pub(crate) style: FlexboxLayout, - /// The mapping from the Size> (in real units) to Size (in points) for this node + /// The mapping from the Size (in real units) to Size (in points) for this node pub(crate) measure: Option, /// The results of the layout computation pub(crate) layout: Layout, @@ -235,7 +235,7 @@ impl Forest { } /// Computes the layout of the `node` and its children - pub(crate) fn compute_layout(&mut self, node: NodeId, size: Size>) { + pub(crate) fn compute_layout(&mut self, node: NodeId, size: Size) { // TODO: It's not clear why this method is distinct self.compute(node, size) } diff --git a/src/geometry.rs b/src/geometry.rs index e043ec180..fd128a2de 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,7 +1,9 @@ //! Geometric primitives useful for layout -use crate::style::{Dimension, FlexDirection}; -use core::ops::Add; +use crate::{ + math::MaybeMath, + style::{Dimension, FlexDirection}, +}; /// An axis-aligned UI rectangle #[derive(Debug, Copy, Clone, PartialEq)] @@ -13,19 +15,19 @@ pub struct Rect { /// /// The starting edge is the left edge when working with LTR text, /// and the right edge when working with RTL text. - pub start: T, + pub start: Option, /// This can represent either the x-coordinate of the ending edge, /// or the amount of padding on the ending side. /// /// The ending edge is the right edge when working with LTR text, /// and the left edge when working with RTL text. - pub end: T, + pub end: Option, /// This can represent either the y-coordinate of the top edge, /// or the amount of padding on the top side. - pub top: T, + pub top: Option, /// This can represent either the y-coordinate of the bottom edge, /// or the amount of padding on the bottom side. - pub bottom: T, + pub bottom: Option, } impl Rect { @@ -34,7 +36,7 @@ impl Rect { /// This is used to transform a `Rect` into a `Rect`. pub(crate) fn map(self, f: F) -> Rect where - F: Fn(T) -> R, + F: Fn(Option) -> Option, { Rect { start: f(self.start), end: f(self.end), top: f(self.top), bottom: f(self.bottom) } } @@ -46,7 +48,7 @@ impl Rect { /// When applied to the top or bottom sides, the height is used instead. pub(crate) fn zip_size(self, size: Size, f: F) -> Rect where - F: Fn(T, U) -> R, + F: Fn(Option, Option) -> Option, U: Copy, { Rect { @@ -60,15 +62,17 @@ impl Rect { impl Rect where - T: Add + Copy + Clone, + T: MaybeMath> + Copy + Clone, + T: MaybeMath, Option> + Copy + Clone, + // T: MaybeMath, T> + Copy + Clone, { /// The sum of [`Rect.start`](Rect) and [`Rect.end`](Rect) /// /// This is typically used when computing total padding. /// /// **NOTE:** this is *not* the width of the rectangle. - pub(crate) fn horizontal_axis_sum(&self) -> T { - self.start + self.end + pub(crate) fn horizontal_axis_sum(&self) -> Option { + self.start.and_then(|start| start.maybe_add(self.end)) } /// The sum of [`Rect.top`](Rect) and [`Rect.bottom`](Rect) @@ -76,8 +80,8 @@ where /// This is typically used when computing total padding. /// /// **NOTE:** this is *not* the height of the rectangle. - pub(crate) fn vertical_axis_sum(&self) -> T { - self.top + self.bottom + pub(crate) fn vertical_axis_sum(&self) -> Option { + self.top.and_then(|top| top.maybe_add(self.bottom)) } /// The sum of the two fields of the [`Rect`] representing the main axis. @@ -86,7 +90,7 @@ where /// /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::horizontal`]. /// Otherwise, this is [`Rect::vertical`]. - pub(crate) fn main_axis_sum(&self, direction: FlexDirection) -> T { + pub(crate) fn main_axis_sum(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.horizontal_axis_sum() } else { @@ -98,7 +102,7 @@ where /// /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::vertical`]. /// Otherwise, this is [`Rect::horizontal`]. - pub(crate) fn cross_axis_sum(&self, direction: FlexDirection) -> T { + pub(crate) fn cross_axis_sum(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.vertical_axis_sum() } else { @@ -112,7 +116,7 @@ where T: Copy + Clone, { /// The `start` or `top` value of the [`Rect`], from the perspective of the main layout axis - pub(crate) fn main_start(&self, direction: FlexDirection) -> T { + pub(crate) fn main_start(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.start } else { @@ -121,7 +125,7 @@ where } /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis - pub(crate) fn main_end(&self, direction: FlexDirection) -> T { + pub(crate) fn main_end(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.end } else { @@ -130,7 +134,7 @@ where } /// The `start` or `top` value of the [`Rect`], from the perspective of the cross layout axis - pub(crate) fn cross_start(&self, direction: FlexDirection) -> T { + pub(crate) fn cross_start(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.top } else { @@ -139,7 +143,7 @@ where } /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis - pub(crate) fn cross_end(&self, direction: FlexDirection) -> T { + pub(crate) fn cross_end(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.bottom } else { @@ -148,21 +152,22 @@ where } } +// 2 dimensional /// The width and height of a [`Rect`] #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(default))] pub struct Size { /// The x extent of the rectangle - pub width: T, + pub width: Option, /// The y extent of the rectangle - pub height: T, + pub height: Option, } impl Size<()> { - /// Generates a `Size>` with undefined width and height + /// Generates a `Size` with undefined width and height #[must_use] - pub fn undefined() -> Size> { + pub fn undefined() -> Size { Size { width: None, height: None } } } @@ -173,7 +178,7 @@ impl Size { /// This is used to transform a `Rect` into a `Rect`. pub fn map(self, f: F) -> Size where - F: Fn(T) -> R, + F: Fn(Option) -> Option, { Size { width: f(self.width), height: f(self.height) } } @@ -181,7 +186,7 @@ impl Size { /// Sets the extent of the main layout axis /// /// Whether this is the width or height depends on the `direction` provided - pub(crate) fn set_main(&mut self, direction: FlexDirection, value: T) { + pub(crate) fn set_main(&mut self, direction: FlexDirection, value: Option) { if direction.is_row() { self.width = value } else { @@ -192,7 +197,7 @@ impl Size { /// Sets the extent of the cross layout axis /// /// Whether this is the width or height depends on the `direction` provided - pub(crate) fn set_cross(&mut self, direction: FlexDirection, value: T) { + pub(crate) fn set_cross(&mut self, direction: FlexDirection, value: Option) { if direction.is_row() { self.height = value } else { @@ -203,7 +208,7 @@ impl Size { /// Gets the extent of the main layout axis /// /// Whether this is the width or height depends on the `direction` provided - pub(crate) fn main(self, direction: FlexDirection) -> T { + pub(crate) fn main(self, direction: FlexDirection) -> Option { if direction.is_row() { self.width } else { @@ -214,7 +219,7 @@ impl Size { /// Gets the extent of the cross layout axis /// /// Whether this is the width or height depends on the `direction` provided - pub(crate) fn cross(self, direction: FlexDirection) -> T { + pub(crate) fn cross(self, direction: FlexDirection) -> Option { if direction.is_row() { self.height } else { @@ -224,17 +229,20 @@ impl Size { } impl Size { - /// A [`Size`] with zero width and height + /// A [`Size`] with zero width and height #[must_use] pub fn zero() -> Self { - Self { width: 0.0, height: 0.0 } + Self { width: Some(0.0), height: Some(0.0) } } } impl Size { /// Converts any `parent`-relative values for size into an absolute size - pub(crate) fn resolve(&self, parent: Size>) -> Size> { - Size { width: self.width.resolve(parent.width), height: self.height.resolve(parent.height) } + pub(crate) fn resolve(&self, parent: Size) -> Size { + Size { + width: if let Some(width) = self.width { width.resolve(parent.width) } else { None }, + height: if let Some(height) = self.height { height.resolve(parent.height) } else { None }, + } } } diff --git a/src/layout.rs b/src/layout.rs index cbf323a90..b37d338fa 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -28,9 +28,9 @@ impl Layout { #[derive(Debug, Clone)] pub(crate) struct Cache { /// The initial cached size of the node itself - pub(crate) node_size: Size>, + pub(crate) node_size: Size, /// The initial cached size of the parent's node - pub(crate) parent_size: Size>, + pub(crate) parent_size: Size, /// Whether or not layout should be recomputed pub(crate) perform_layout: bool, diff --git a/src/math.rs b/src/math.rs index 2d99ab827..27c2ce956 100644 --- a/src/math.rs +++ b/src/math.rs @@ -23,6 +23,7 @@ impl MaybeMath, Option> for Option { match (self, rhs) { (Some(l), Some(r)) => Some(l.min(r)), (Some(_l), None) => self, + (None, Some(_r)) => rhs, (None, Some(_r)) => None, (None, None) => None, } @@ -121,3 +122,10 @@ impl MaybeMath, f32> for f32 { } } } + +pub(crate) trait MaybeAxisSum { + fn horizontal_axis_sum(self, rhs: In) -> Out; + fn vertical_axis_sum(self, rhs: In) -> Out; + fn main_axis_sum(self, rhs: In) -> Out; + fn cross_axis_sum(self, rhs: In) -> Out; +} diff --git a/src/node.rs b/src/node.rs index d92ea10cf..96329f411 100644 --- a/src/node.rs +++ b/src/node.rs @@ -11,13 +11,13 @@ use crate::sys::Box; use crate::sys::{new_map_with_capacity, ChildrenVec, Map, Vec}; use core::sync::atomic::{AtomicUsize, Ordering}; -/// A function that can be applied to a `Size>` to obtain a `Size` +/// A function that can be applied to a `Size` to obtain a `Size` pub enum MeasureFunc { /// Stores an unboxed function - Raw(fn(Size>) -> Size), + Raw(fn(Size) -> Size), /// Stores a boxed function #[cfg(any(feature = "std", feature = "alloc"))] - Boxed(Box>) -> Size>), + Boxed(Box) -> Size>), } /// Global taffy instance id allocator. @@ -287,7 +287,7 @@ impl Taffy { } /// Updates the stored layout of the provided `node` and its children - pub fn compute_layout(&mut self, node: Node, size: Size>) -> Result<(), error::InvalidNode> { + pub fn compute_layout(&mut self, node: Node, size: Size) -> Result<(), error::InvalidNode> { let id = self.find_node(node)?; self.forest.compute_layout(id, size); Ok(()) diff --git a/src/style.rs b/src/style.rs index 22f5a03f7..678e2f145 100644 --- a/src/style.rs +++ b/src/style.rs @@ -247,12 +247,10 @@ impl Default for FlexWrap { /// A unit of linear measurement /// /// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size`]. -/// The default value is [`Dimension::Undefined`]. +/// The default value is [`Dimension::Auto`]. #[derive(Copy, Clone, PartialEq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Dimension { - /// The dimension is not given - Undefined, /// The dimension should be automatically computed Auto, /// The dimension is stored in [points](https://en.wikipedia.org/wiki/Point_(typography)) @@ -265,25 +263,20 @@ pub enum Dimension { impl Default for Dimension { fn default() -> Self { - Self::Undefined + Self::Auto } } impl Dimension { - /// Converts the given [`Dimension`] into a concrete value of points + /// Converts the given [`Dimension`] into a concrete value of points based on the dimension of the parent pub(crate) fn resolve(self, parent_dim: Option) -> Option { match self { Dimension::Points(points) => Some(points), // parent_dim * percent Dimension::Percent(percent) => parent_dim.map(|dim| dim * percent), - _ => None, + Dimension::Auto => None, } } - - /// Is this value defined? - pub(crate) fn is_defined(self) -> bool { - matches!(self, Dimension::Points(_) | Dimension::Percent(_)) - } } impl Default for Rect { @@ -294,7 +287,7 @@ impl Default for Rect { impl Default for Size { fn default() -> Self { - Self { width: Dimension::Auto, height: Dimension::Auto } + Self { width: Default::default(), height: Default::default() } } } @@ -380,7 +373,7 @@ impl Default for FlexboxLayout { border: Default::default(), flex_grow: 0.0, flex_shrink: 1.0, - flex_basis: Dimension::Auto, + flex_basis: Default::default(), size: Default::default(), min_size: Default::default(), max_size: Default::default(), @@ -391,7 +384,7 @@ impl Default for FlexboxLayout { impl FlexboxLayout { /// If the `direction` is row-oriented, the min width. Otherwise the min height - pub(crate) fn min_main_size(&self, direction: FlexDirection) -> Dimension { + pub(crate) fn min_main_size(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.min_size.width } else { @@ -400,7 +393,7 @@ impl FlexboxLayout { } /// If the `direction` is row-oriented, the max width. Otherwise the max height - pub(crate) fn max_main_size(&self, direction: FlexDirection) -> Dimension { + pub(crate) fn max_main_size(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.max_size.width } else { @@ -409,7 +402,7 @@ impl FlexboxLayout { } /// If the `direction` is row-oriented, the margin start. Otherwise the margin top - pub(crate) fn main_margin_start(&self, direction: FlexDirection) -> Dimension { + pub(crate) fn main_margin_start(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.margin.start } else { @@ -418,7 +411,7 @@ impl FlexboxLayout { } /// If the `direction` is row-oriented, the margin end. Otherwise the margin bottom - pub(crate) fn main_margin_end(&self, direction: FlexDirection) -> Dimension { + pub(crate) fn main_margin_end(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.margin.end } else { @@ -427,7 +420,7 @@ impl FlexboxLayout { } /// If the `direction` is row-oriented, the height. Otherwise the width - pub(crate) fn cross_size(&self, direction: FlexDirection) -> Dimension { + pub(crate) fn cross_size(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.size.height } else { @@ -436,7 +429,7 @@ impl FlexboxLayout { } /// If the `direction` is row-oriented, the min height. Otherwise the min width - pub(crate) fn min_cross_size(&self, direction: FlexDirection) -> Dimension { + pub(crate) fn min_cross_size(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.min_size.height } else { @@ -445,7 +438,7 @@ impl FlexboxLayout { } /// If the `direction` is row-oriented, the max height. Otherwise the max width - pub(crate) fn max_cross_size(&self, direction: FlexDirection) -> Dimension { + pub(crate) fn max_cross_size(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.max_size.height } else { @@ -454,7 +447,7 @@ impl FlexboxLayout { } /// If the `direction` is row-oriented, the margin top. Otherwise the margin start - pub(crate) fn cross_margin_start(&self, direction: FlexDirection) -> Dimension { + pub(crate) fn cross_margin_start(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.margin.top } else { @@ -463,7 +456,7 @@ impl FlexboxLayout { } /// If the `direction` is row-oriented, the margin bottom. Otherwise the margin end - pub(crate) fn cross_margin_end(&self, direction: FlexDirection) -> Dimension { + pub(crate) fn cross_margin_end(&self, direction: FlexDirection) -> Option { if direction.is_row() { self.margin.bottom } else { @@ -485,4 +478,12 @@ impl FlexboxLayout { self.align_self } } + + /// Returns true if at least one dimension is `Some` on `min_size` or `max_size` + pub(crate) fn has_defined_size(&self) -> bool { + self.min_size.width.is_some() + || self.min_size.height.is_some() + || self.max_size.width.is_some() + || self.max_size.height.is_some() + } }