From d6c3a1e5f65c3cb945f0b9a9739f640726c17238 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 8 Jun 2022 15:25:23 -0400 Subject: [PATCH 01/35] Deny missing docs :3 --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 654384fa4..563c24105 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![forbid(unsafe_code)] +#![warn(missing_docs)] +#![warn(clippy::missing_docs_in_private_items)] #[cfg(all(not(feature = "std"), feature = "alloc"))] extern crate alloc; From 559ac6a0abfe1d889f6b61a73dba68eb6d745b9a Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 8 Jun 2022 15:31:55 -0400 Subject: [PATCH 02/35] Module level docs --- src/geometry.rs | 2 ++ src/lib.rs | 1 + src/node.rs | 7 +++++-- src/number.rs | 2 ++ src/result.rs | 1 + src/style.rs | 2 ++ src/sys.rs | 2 ++ 7 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/geometry.rs b/src/geometry.rs index 2f94a1e0c..090c2230c 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,3 +1,5 @@ +//! Geometric primitives useful for layout + use core::ops::Add; use crate::number::Number; diff --git a/src/lib.rs b/src/lib.rs index 563c24105..7d627efe1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![doc = include_str!("../README.md")] #![cfg_attr(not(feature = "std"), no_std)] #![forbid(unsafe_code)] #![warn(missing_docs)] diff --git a/src/node.rs b/src/node.rs index 19f1881f3..6ce379f92 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,5 +1,6 @@ -use core::ops::Drop; - +//! UI [`Node`] types and related data structures. +//! +//! Layouts are composed of multiple nodes, which live in a forest-like data structure. use crate::forest::Forest; use crate::geometry::Size; use crate::id::{Allocator, Id, NodeId}; @@ -10,6 +11,7 @@ use crate::style::Style; use crate::sys::Box; use crate::sys::{new_map_with_capacity, ChildrenVec, Map, Vec}; use crate::Error; +use core::ops::Drop; pub enum MeasureFunc { Raw(fn(Size) -> Size), @@ -20,6 +22,7 @@ pub enum MeasureFunc { /// Global sprawl instance id allocator. static INSTANCE_ALLOCATOR: Allocator = Allocator::new(); +/// An [`Id`]-containing identifier #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(not(any(feature = "std", feature = "alloc")), derive(hash32_derive::Hash32))] pub struct Node { diff --git a/src/number.rs b/src/number.rs index 53898eeab..222134db3 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,3 +1,5 @@ +//! Contains the [`Number`] type and associated methods + use core::ops::{Add, Div, Mul, Sub}; #[derive(Copy, Clone, PartialEq, Debug)] diff --git a/src/result.rs b/src/result.rs index ff3619fe3..95a9bf65e 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,3 +1,4 @@ +//! The final results of a layout algorithm. use crate::algo::ComputeResult; use crate::geometry::{Point, Size}; use crate::number::Number; diff --git a/src/style.rs b/src/style.rs index bb831b6dd..1a5176002 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,3 +1,5 @@ +//! A representation of the [CSS style attribute](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) in Rust, used for flexbox layout + use crate::geometry::{Rect, Size}; use crate::number::Number; diff --git a/src/sys.rs b/src/sys.rs index 47921a38e..e8b012f60 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -1,3 +1,5 @@ +//! Allocator-flexible data types + // When std is enabled, prefer those types #[cfg(feature = "std")] pub use self::std::*; From 6dfc13b44b451cb611644a291b2531a170f9e604 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 8 Jun 2022 17:54:31 -0400 Subject: [PATCH 03/35] Document geometry.rs --- src/geometry.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/geometry.rs b/src/geometry.rs index 090c2230c..467b63378 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -5,23 +5,37 @@ use core::ops::Add; use crate::number::Number; use crate::style::{Dimension, FlexDirection}; +/// The spatial extent of an axis-aligned UI rectangle #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(default))] pub struct Rect { + /// The x-coordinate of the left edge pub start: T, + /// The x-coordinate of the right edge pub end: T, + /// The y-coordinate of the top edge pub top: T, + /// the y-coordinate of the bottom edge pub bottom: T, } impl Rect { + /// Applies the function `f` to all four sides of the [`Rect`] + /// + /// This is used to transform a `Rect` into a `Rect`. pub(crate) fn map(self, f: F) -> Rect where F: Fn(T) -> R, { Rect { start: f(self.start), end: f(self.end), top: f(self.top), bottom: f(self.bottom) } } + + /// Applies the function `f` to all four sides of the rect + /// + /// When applied to the left and right sides, the width is used + /// as the second parameter of `f`. + /// 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, @@ -40,14 +54,26 @@ impl Rect where T: Add + Copy + Clone, { + /// The sum of the coordinates of the left and right side of the rectangle + /// + /// **NOTE:** this is *not* the width of the rectangle. pub(crate) fn horizontal(&self) -> T { self.start + self.end } + /// The sum of the coordinates of the bottom and top side of the rectangle + /// + /// **NOTE:** this is *not* the height of the rectangle. pub(crate) fn vertical(&self) -> T { self.top + self.bottom } + /// The main layout-axis sum of the rectangle + /// + /// This is always perpendicular to [`Rect::cross`]. + /// + /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::horizontal`]. + /// Otherwise, this is [`Rect::vertical`]. pub(crate) fn main(&self, direction: FlexDirection) -> T { if direction.is_row() { self.horizontal() @@ -56,6 +82,12 @@ where } } + /// The cross layout-axis sum of the rectangle + /// + /// This is always perpendicular to [`Rect::main`]. + /// + /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::vertical`]. + /// Otherwise, this is [`Rect::horizontal`]. pub(crate) fn cross(&self, direction: FlexDirection) -> T { if direction.is_row() { self.vertical() @@ -69,6 +101,7 @@ impl Rect where T: Copy + Clone, { + /// The lowest coordinate of the rectangle, from the perspective of the main layout axis pub(crate) fn main_start(&self, direction: FlexDirection) -> T { if direction.is_row() { self.start @@ -77,6 +110,7 @@ where } } + /// The highest coordinate of the rectangle, from the perspective of the main layout axis pub(crate) fn main_end(&self, direction: FlexDirection) -> T { if direction.is_row() { self.end @@ -85,6 +119,7 @@ where } } + /// The lowest coordinate of the rectangle, from the perspective of the cross layout axis pub(crate) fn cross_start(&self, direction: FlexDirection) -> T { if direction.is_row() { self.top @@ -93,6 +128,7 @@ where } } + /// The highest coordinate of the rectangle, from the perspective of the cross layout axis pub(crate) fn cross_end(&self, direction: FlexDirection) -> T { if direction.is_row() { self.bottom @@ -102,21 +138,28 @@ where } } +/// 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, + /// The y extent of the rectangle pub height: T, } impl Size<()> { + /// Generates a `Size` with undefined width and height pub fn undefined() -> Size { Size { width: Number::Undefined, height: Number::Undefined } } } impl Size { + /// Applies the function `f` to both the width and height + /// + /// This is used to transform a `Rect` into a `Rect`. pub fn map(self, f: F) -> Size where F: Fn(T) -> R, @@ -124,6 +167,9 @@ impl Size { Size { width: f(self.width), height: f(self.height) } } + /// 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) { if direction.is_row() { self.width = value @@ -132,6 +178,9 @@ 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) { if direction.is_row() { self.height = value @@ -140,6 +189,9 @@ 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 { if direction.is_row() { self.width @@ -148,6 +200,9 @@ 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 { if direction.is_row() { self.height @@ -158,6 +213,7 @@ impl Size { } impl Size { + /// A [`Size`] with zero width and height pub fn zero() -> Self { Self { width: 0.0, height: 0.0 } } @@ -169,13 +225,19 @@ impl Size { } } +/// A 2-dimensional coordinate. +/// +/// When used in association with a [`Rect`], represents the bottom-left corner. #[derive(Debug, Copy, Clone, PartialEq)] pub struct Point { + /// The x-coordinate pub x: T, + /// The y-coordinate pub y: T, } impl Point { + /// A [`Point`] with values (0,0), representing the origin pub fn zero() -> Self { Self { x: 0.0, y: 0.0 } } From 851a7fd9766d71a54cbf5014ab6ddfc9e2820883 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 8 Jun 2022 18:14:29 -0400 Subject: [PATCH 04/35] Document layout.rs --- src/layout.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index f4be8c371..da5c7aef0 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,3 +1,5 @@ +//! Final and cached data structures that represent the high-level UI layout + use crate::flexbox::ComputeResult; use crate::geometry::{Point, Size}; use crate::number::Number; @@ -10,23 +12,29 @@ pub struct Layout { /// 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 width and height of the node + /// The width and height of the node pub size: Size, - // The bottom-left corner of the node + /// The bottom-left corner of the node pub location: Point, } impl Layout { + /// Creates a new [`Layout`] struct with zero size positioned at the origin pub(crate) fn new() -> Self { Self { order: 0, size: Size::zero(), location: Point::zero() } } } +/// Cached intermediate layout results #[derive(Debug, Clone)] pub(crate) struct Cache { + /// The initial cached size of the node itself pub(crate) node_size: Size, + /// The initial cached size of the parent's node pub(crate) parent_size: Size, + /// Whether or not layout should be recomputed pub(crate) perform_layout: bool, + /// The stored result of the layout calculations pub(crate) result: ComputeResult, } From 37245f380b97f16e2ea50a3ac83dcc8d137eef9f Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 8 Jun 2022 18:16:49 -0400 Subject: [PATCH 05/35] Document prelude.rs --- src/prelude.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/prelude.rs b/src/prelude.rs index a9eec8986..c2d66c2e7 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,3 +1,5 @@ +//! Commonly used types + pub use crate::{ geometry::{Rect, Size}, layout::Layout, From b4c118d2a2f2d55d51b89932650fabb938e32481 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 8 Jun 2022 18:18:30 -0400 Subject: [PATCH 06/35] Document lib.rs --- src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 71c6e617c..fe28594d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,13 +22,16 @@ mod flexbox; mod forest; mod sys; +pub use crate::node::Sprawl; + #[cfg(feature = "std")] use core::fmt::{Display, Formatter, Result}; -pub use crate::node::Sprawl; - +/// An error that can occur when performing layout +#[non_exhaustive] #[derive(Debug)] pub enum Error { + /// The [`Node`](node::Node) was invalid InvalidNode(node::Node), } @@ -45,7 +48,7 @@ impl Display for Error { impl std::error::Error for Error { fn description(&self) -> &str { match *self { - Error::InvalidNode(_) => "The node is not part of the sprawl instance", + Error::InvalidNode(_) => "The node is not part of the Sprawl instance", } } } From 3541aa5522a843f128677372a4433a7cc78c93e8 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 8 Jun 2022 19:11:17 -0400 Subject: [PATCH 07/35] Document node.rs --- src/node.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/src/node.rs b/src/node.rs index 35db35608..b2d077f53 100644 --- a/src/node.rs +++ b/src/node.rs @@ -12,8 +12,11 @@ use crate::sys::{new_map_with_capacity, ChildrenVec, Map, Vec}; use crate::Error; use core::sync::atomic::{AtomicUsize, Ordering}; +/// A function that can be applied to a `Size` to obetain a `` pub enum MeasureFunc { + /// Stores a unboxed function Raw(fn(Size) -> Size), + /// Stores a boxed function #[cfg(any(feature = "std", feature = "alloc"))] Boxed(Box) -> Size>), } @@ -25,15 +28,23 @@ static INSTANCE_ALLOCATOR: Allocator = Allocator::new(); #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(not(any(feature = "std", feature = "alloc")), derive(hash32_derive::Hash32))] pub struct Node { + /// The id of the forest that this node lives with instance: Id, + /// The identifier of this particular node within the tree local: Id, } +/// A forest of UI [`Nodes`](`Node`), suitable for UI layout pub struct Sprawl { + /// The ID of the root node id: Id, - nodes: Allocator, + /// A monotonically-increasing index that tracks the [`Id`] of the next node + allocator: Allocator, + /// A map from Node -> NodeId nodes_to_ids: Map, + /// A map from NodeId -> Node ids_to_nodes: Map, + /// An efficient data structure that stores the node trees forest: Forest, } @@ -44,31 +55,37 @@ impl Default for Sprawl { } impl Sprawl { + /// Creates a new [`Sprawl`] + /// + /// The default capacity of a [`Sprawl`] is 16 nodes. pub fn new() -> Self { Default::default() } + /// Creates a new [`Sprawl`] that can store `capacity` nodes before reallocation pub fn with_capacity(capacity: usize) -> Self { Self { id: INSTANCE_ALLOCATOR.allocate(), - nodes: Allocator::new(), + allocator: Allocator::new(), nodes_to_ids: new_map_with_capacity(capacity), ids_to_nodes: new_map_with_capacity(capacity), forest: Forest::with_capacity(capacity), } } + /// Allocates memory for a new node, and returns a matching generated [`Node`] fn allocate_node(&mut self) -> Node { - let local = self.nodes.allocate(); + let local = self.allocator.allocate(); Node { instance: self.id, local } } + /// Stores a new fn add_node(&mut self, node: Node, id: NodeId) { let _ = self.nodes_to_ids.insert(node, id); let _ = self.ids_to_nodes.insert(id, node); } - // Find node in the forest. + // Returns the `NodeId` of the provided node within the forest fn find_node(&self, node: Node) -> Result { match self.nodes_to_ids.get(&node) { Some(id) => Ok(*id), @@ -76,6 +93,7 @@ impl Sprawl { } } + /// Adds a new leaf node, which does not have any children pub fn new_leaf(&mut self, style: Style, measure: MeasureFunc) -> Result { let node = self.allocate_node(); let id = self.forest.new_leaf(style, measure); @@ -83,6 +101,7 @@ impl Sprawl { Ok(node) } + /// Adds a new node, which may have any number of `children` pub fn new_node(&mut self, style: Style, children: &[Node]) -> Result { let node = self.allocate_node(); let children = @@ -92,19 +111,21 @@ impl Sprawl { Ok(node) } - /// Removes all nodes. + /// Removes all nodes /// - /// All associated nodes will be invalid. + /// All associated [`Id`] will be rendered invalid. pub fn clear(&mut self) { for node in self.nodes_to_ids.keys() { - self.nodes.free(&[node.local]); + self.allocator.free(&[node.local]); } self.nodes_to_ids.clear(); self.ids_to_nodes.clear(); self.forest.clear(); } - /// Remove nodes. + /// Remove a specific [`Node`] from the tree + /// + /// Its [`Id`] is rendered invalid pub fn remove(&mut self, node: Node) { let id = if let Ok(id) = self.find_node(node) { id } else { return }; @@ -118,6 +139,7 @@ impl Sprawl { } } + /// Sets the [`MeasureFunc`] of the associated node pub fn set_measure(&mut self, node: Node, measure: Option) -> Result<(), Error> { let id = self.find_node(node)?; self.forest.nodes[id].measure = measure; @@ -125,6 +147,7 @@ impl Sprawl { Ok(()) } + /// Adds a `child` [`Node`] under the supplied `node` pub fn add_child(&mut self, node: Node, child: Node) -> Result<(), Error> { let node_id = self.find_node(node)?; let child_id = self.find_node(child)?; @@ -133,6 +156,7 @@ impl Sprawl { Ok(()) } + /// Directly sets the `children` of the supplied `Node` pub fn set_children(&mut self, node: Node, children: &[Node]) -> Result<(), Error> { let node_id = self.find_node(node)?; let children_id = children.iter().map(|child| self.find_node(*child)).collect::, _>>()?; @@ -152,6 +176,9 @@ impl Sprawl { Ok(()) } + /// Removes the `child` of the parent `node` + /// + /// The child is not removed from the forest entirely, it is simply no longer attached to its previous parent. pub fn remove_child(&mut self, node: Node, child: Node) -> Result { let node_id = self.find_node(node)?; let child_id = self.find_node(child)?; @@ -160,6 +187,9 @@ impl Sprawl { Ok(self.ids_to_nodes[&prev_id]) } + /// Removes the "n-th" child from the parent `node` + /// + /// The child is not removed from the forest entirely, it is simply no longer attached to its previous parent. pub fn remove_child_at_index(&mut self, node: Node, index: usize) -> Result { let node_id = self.find_node(node)?; // TODO: index check @@ -168,6 +198,9 @@ impl Sprawl { Ok(self.ids_to_nodes[&prev_id]) } + /// Replaces the "n-th" child from the parent `node` with the new `child` node + /// + /// The child is not removed from the forest entirely, it is simply no longer attached to its previous parent. pub fn replace_child_at_index(&mut self, node: Node, index: usize, child: Node) -> Result { let node_id = self.find_node(node)?; let child_id = self.find_node(child)?; @@ -182,21 +215,25 @@ impl Sprawl { Ok(self.ids_to_nodes[&old_child]) } + /// Returns a list of children of the given [`Node`] pub fn children(&self, node: Node) -> Result, Error> { let id = self.find_node(node)?; Ok(self.forest.children[id].iter().map(|child| self.ids_to_nodes[child]).collect()) } + /// Returns the child [`Node`] of the parent `node` at the provided `index` pub fn child_at_index(&self, node: Node, index: usize) -> Result { let id = self.find_node(node)?; Ok(self.ids_to_nodes[&self.forest.children[id][index]]) } + /// Returns the number of children of the parent `node` pub fn child_count(&self, node: Node) -> Result { let id = self.find_node(node)?; Ok(self.forest.children[id].len()) } + /// Sets the [`Style`] of the provided `node` pub fn set_style(&mut self, node: Node, style: Style) -> Result<(), Error> { let id = self.find_node(node)?; self.forest.nodes[id].style = style; @@ -204,6 +241,7 @@ impl Sprawl { Ok(()) } + /// Gets the [`Style`] of the provided `node` pub fn style(&self, node: Node) -> Result<&Style, Error> { let id = self.find_node(node)?; Ok(&self.forest.nodes[id].style) @@ -215,17 +253,20 @@ impl Sprawl { Ok(&self.forest.nodes[id].layout) } + /// Indicates that the layout of this node and its children must be recomputed pub fn mark_dirty(&mut self, node: Node) -> Result<(), Error> { let id = self.find_node(node)?; self.forest.mark_dirty(id); Ok(()) } + /// Does the layout of this node (and its children) need to be recomputed pub fn dirty(&self, node: Node) -> Result { let id = self.find_node(node)?; Ok(self.forest.nodes[id].is_dirty) } + /// Updates the stored layout of the provided `node` and its children pub fn compute_layout(&mut self, node: Node, size: Size) -> Result<(), Error> { let id = self.find_node(node)?; self.forest.compute_layout(id, size); @@ -242,22 +283,29 @@ impl Drop for Sprawl { /// Internal node id. pub(crate) type NodeId = usize; +/// The identifier of a [`Node`] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(not(any(feature = "std", feature = "alloc")), derive(hash32_derive::Hash32))] pub(crate) struct Id(usize); +/// An bump-allocator index that tracks how many [`Nodes`](Node) have been allocated in a [`Sprawl`]. pub(crate) struct Allocator { new_id: AtomicUsize, } impl Allocator { + /// Creates a fresh [`Allocator`] pub const fn new() -> Self { Self { new_id: AtomicUsize::new(0) } } + /// Allocates space for one more [`Node`] pub fn allocate(&self) -> Id { Id(self.new_id.fetch_add(1, Ordering::Relaxed)) } + /// Frees [`Ids`](Id) from the allocator + /// + /// NOTE: this does not actually free memory in any way. pub fn free(&self, _ids: &[Id]) {} } From cce66c071d065facbd256dcc719046b2e58eb9b2 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 8 Jun 2022 19:25:18 -0400 Subject: [PATCH 08/35] Document number.rs --- src/number.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/number.rs b/src/number.rs index 222134db3..bdf76f088 100644 --- a/src/number.rs +++ b/src/number.rs @@ -2,14 +2,24 @@ use core::ops::{Add, Div, Mul, Sub}; +/// A [`f32`] that may be undefined +/// +/// NOTE: this should *really* be an [`Option`], +/// but we're optimizing for backwards compatibility for now. #[derive(Copy, Clone, PartialEq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Number { + /// Pretend this is [`Some(f32)`] + /// + /// WARNING: the contained value may be NaN Defined(f32), + /// Pretend this is [`None`] Undefined, } +/// An extension method used for [`Number`] pub trait OrElse { + /// Returns the contained value if it is defined, otherwise returns `other` fn or_else(self, other: T) -> T; } @@ -38,6 +48,7 @@ impl OrElse for Number { } impl Number { + /// Is the number defined? pub fn is_defined(self) -> bool { match self { Number::Defined(_) => true, @@ -45,6 +56,7 @@ impl Number { } } + /// Is the number undefined? pub fn is_undefined(self) -> bool { match self { Number::Defined(_) => false, @@ -53,8 +65,15 @@ impl Number { } } +/// An extension trait for [`Number`] pub trait MinMax { + /// Returns the minimum of self and rhs + /// + /// If either either value is invalid, returns the other value. fn maybe_min(self, rhs: In) -> Out; + /// Returns the maximum of self and rhs + /// + /// If either either value is invalid, returns the other value. fn maybe_max(self, rhs: In) -> Out; } From 82e478a43d2857912498c79b3eba7d9a22d2b4ee Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 8 Jun 2022 20:26:56 -0400 Subject: [PATCH 09/35] Partial docs for style.rs --- src/style.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/style.rs b/src/style.rs index 1a5176002..11877bd8e 100644 --- a/src/style.rs +++ b/src/style.rs @@ -3,13 +3,21 @@ use crate::geometry::{Rect, Size}; use crate::number::Number; +/// How [`Nodes`](crate::node::Node) are aligned relative to the cross axis +/// +/// The default behavior is [`AlignItems::Stretch`]. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum AlignItems { + /// Items are packed toward the start of the cross axis FlexStart, + /// Items are packed toward the end of the cross axis FlexEnd, + /// Items are packed along the center of the cross axis Center, + /// Items are aligned such as their baselines align Baseline, + /// Stretch to fill the container Stretch, } @@ -19,14 +27,23 @@ impl Default for AlignItems { } } +/// Allows the alignment specified by [`AlignItems`] to be overridden by child [`Nodes`](crate::node::Node). +/// +/// The default behavior is [`AlignSelf::Auto`]. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum AlignSelf { + /// Inherits the [`AlignItems`] behavior of the parent Auto, + /// Items are packed toward the start of the cross axis FlexStart, + /// Items are packed toward the end of the cross axis FlexEnd, + /// Items are packed along the center of the cross axis Center, + /// Items are aligned such as their baselines align Baseline, + /// Stretch to fill the container Stretch, } @@ -56,6 +73,7 @@ impl Default for AlignContent { #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Direction { + /// Inherits the [`Direction`] defined by the parent Inherit, #[cfg_attr(feature = "serde", serde(rename = "ltr"))] LTR, @@ -84,12 +102,32 @@ impl Default for Display { } } +/// The direction in which the flexbox layout should be defined relative to. +/// +/// There are always two perpendicular layout axes: main (or primary) and cross (or secondary). +/// Items will be laid out along the main axis (which can then be swapped for the children). +/// +/// Items are always aligned relative to the cross axis, and justified relative to the main axis. +/// +/// The default behavior is [`FlexDirection::Row`]. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum FlexDirection { + /// Defines +x as the main axis + /// + /// Items will be added from left to right in a row. Row, + /// Defines +y as the main axis + /// + /// Items will be added from top to bottom in a column. Column, + /// Defines -x as the main axis + /// + /// Items will be added from right to left in a row. RowReverse, + /// Defines -y as the main axis + /// + /// Items will be added from bottom to top in a column. ColumnReverse, } From ff8f13fda273a90acbe2b62049e243d06057add4 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 8 Jun 2022 21:07:43 -0400 Subject: [PATCH 10/35] Warn that padding broken :( --- src/style.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/style.rs b/src/style.rs index 11877bd8e..9ffefccac 100644 --- a/src/style.rs +++ b/src/style.rs @@ -269,6 +269,7 @@ pub struct Style { pub justify_content: JustifyContent, pub position: Rect, pub margin: Rect, + /// WARNING: this currently has no effect! pub padding: Rect, pub border: Rect, pub flex_grow: f32, From 058faa23709f034c242788f25fdae9886dcbec04 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 8 Jun 2022 23:01:28 -0400 Subject: [PATCH 11/35] Add docs to forest.rs --- src/forest.rs | 52 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/src/forest.rs b/src/forest.rs index e03b3fa88..9d3655ea1 100644 --- a/src/forest.rs +++ b/src/forest.rs @@ -1,4 +1,4 @@ -//! Forest - ECS like datastructure for storing node trees. +//! Forest - a struct-of-arrays data structure for storing node trees. //! //! Backing datastructure for `Sprawl` structs. use crate::geometry::Size; @@ -8,6 +8,9 @@ use crate::number::Number; use crate::style::Style; use crate::sys::{new_vec_with_capacity, ChildrenVec, ParentsVec, Vec}; +/// Layout information for a given [`Node`](crate::node::Node) +/// +/// Stored in a [`Forest`]. pub(crate) struct NodeData { pub(crate) style: Style, pub(crate) measure: Option, @@ -18,6 +21,7 @@ pub(crate) struct NodeData { } impl NodeData { + /// Create the data for a new leaf node fn new_leaf(style: Style, measure: MeasureFunc) -> Self { Self { style, @@ -29,6 +33,8 @@ impl NodeData { } } + /// Create the data for a new node + // TODO: why is this different from new_leaf? fn new(style: Style) -> Self { Self { style, @@ -41,6 +47,7 @@ impl NodeData { } } +/// A collection of UI layout trees used to store [`NodeData`] associated with specific [`Nodes`](crate::node::Node) pub(crate) struct Forest { pub(crate) nodes: Vec, pub(crate) children: Vec>, @@ -48,7 +55,8 @@ pub(crate) struct Forest { } impl Forest { - pub fn with_capacity(capacity: usize) -> Self { + /// Creates a new [`Forest`] that can store up to `capacity` [`Nodes`](crate::node::Node) before needing to be expanded + pub(crate) fn with_capacity(capacity: usize) -> Self { Self { nodes: new_vec_with_capacity(capacity), children: new_vec_with_capacity(capacity), @@ -56,7 +64,8 @@ impl Forest { } } - pub fn new_leaf(&mut self, style: Style, measure: MeasureFunc) -> NodeId { + /// Adds a new unattached leaf node to the forest, and returns the [`NodeId`] of the new node + pub(crate) fn new_leaf(&mut self, style: Style, measure: MeasureFunc) -> NodeId { let id = self.nodes.len(); self.nodes.push(NodeData::new_leaf(style, measure)); self.children.push(new_vec_with_capacity(0)); @@ -64,7 +73,8 @@ impl Forest { id } - pub fn new_node(&mut self, style: Style, children: ChildrenVec) -> NodeId { + /// Adds a new unparented node to the forest with the associated children attached, and returns the [`NodeId`] of the new node + pub(crate) fn new_node(&mut self, style: Style, children: ChildrenVec) -> NodeId { let id = self.nodes.len(); for child in &children { self.parents[*child].push(id); @@ -75,20 +85,27 @@ impl Forest { id } - pub fn add_child(&mut self, node: NodeId, child: NodeId) { + /// Adds a `child` node to the parent `node` + pub(crate) fn add_child(&mut self, node: NodeId, child: NodeId) { self.parents[child].push(node); self.children[node].push(child); self.mark_dirty(node) } - pub fn clear(&mut self) { + /// Removes all nodes and resets the data structure + /// + /// The capacity is retained. + pub(crate) fn clear(&mut self) { self.nodes.clear(); self.children.clear(); self.parents.clear(); } - /// Removes a node and swaps with the last node. - pub fn swap_remove(&mut self, node: NodeId) -> Option { + /// Removes the specified `node` + /// + /// The last existing node is moved to its previous position, in order to ensure compactness. + /// Returns the previous [`NodeId`] of the moved node, if one was moved. + pub(crate) fn swap_remove(&mut self, node: NodeId) -> Option { self.nodes.swap_remove(node); // Now the last element is swapped in at index `node`. @@ -156,19 +173,28 @@ impl Forest { } } - pub fn remove_child(&mut self, node: NodeId, child: NodeId) -> NodeId { + /// Breaks the link between the parent `node` and the `child` node + /// + /// The `child`'s data is not removed. + pub(crate) fn remove_child(&mut self, node: NodeId, child: NodeId) -> NodeId { let index = self.children[node].iter().position(|n| *n == child).unwrap(); self.remove_child_at_index(node, index) } - pub fn remove_child_at_index(&mut self, node: NodeId, index: usize) -> NodeId { + /// Breaks the link between the parent `node` and the n-th child node + /// + /// The child's data is not removed. + pub(crate) fn remove_child_at_index(&mut self, node: NodeId, index: usize) -> NodeId { let child = self.children[node].remove(index); self.parents[child].retain(|p| *p != node); self.mark_dirty(node); child } - pub fn mark_dirty(&mut self, node: NodeId) { + /// Marks the `node` as needing layout recalculation + /// + /// Any cached layout information is cleared. + pub(crate) fn mark_dirty(&mut self, node: NodeId) { fn mark_dirty_impl(nodes: &mut Vec, parents: &[ParentsVec], node_id: NodeId) { let node = &mut nodes[node_id]; node.main_size_layout_cache = None; @@ -183,7 +209,9 @@ impl Forest { mark_dirty_impl(&mut self.nodes, &self.parents, node); } - pub fn compute_layout(&mut self, node: NodeId, size: Size) { + /// Computes the layout of the `node` and its children + 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) } } From 75546ccb52a6ef4adfd486e7c555ff9637f378c8 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 10:58:57 -0400 Subject: [PATCH 12/35] Fix typo Co-authored-by: Andreas Weibye <13300393+Weibye@users.noreply.github.com> --- src/node.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node.rs b/src/node.rs index ff250dc3b..345551cef 100644 --- a/src/node.rs +++ b/src/node.rs @@ -85,7 +85,7 @@ impl Sprawl { let _ = self.ids_to_nodes.insert(id, node); } - // Returns the `NodeId` of the provided node within the forest + /// Returns the `NodeId` of the provided node within the forest fn find_node(&self, node: Node) -> Result { match self.nodes_to_ids.get(&node) { Some(id) => Ok(*id), From 21c5fb55d7ca011492a7f11d7437b9278fdee298 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 12:24:32 -0400 Subject: [PATCH 13/35] Fix docs for geometry.rs --- src/geometry.rs | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/geometry.rs b/src/geometry.rs index 467b63378..ec1f66f66 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -5,18 +5,28 @@ use core::ops::Add; use crate::number::Number; use crate::style::{Dimension, FlexDirection}; -/// The spatial extent of an axis-aligned UI rectangle +/// An axis-aligned UI rectangle #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(default))] pub struct Rect { - /// The x-coordinate of the left edge + /// This can represent either the x-coordinate of the starting edge, + /// or the amount of padding on the starting side. + /// + /// The starting edge is the left edge when working with LTR text, + /// and the right edge when working with RTL text. pub start: T, - /// The x-coordinate of the right edge + /// 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, - /// The y-coordinate of the top edge + /// This can represent either the y-coordinate of the top edge, + /// or the amount of padding on the top side. pub top: T, - /// the y-coordinate of the bottom edge + /// This can represent either the y-coordinate of the bottom edge, + /// or the amount of padding on the bottom side. pub bottom: T, } @@ -54,23 +64,27 @@ impl Rect where T: Add + Copy + Clone, { - /// The sum of the coordinates of the left and right side of the rectangle + /// The sum of [`Rect.left`](Rect) and [`Rect.right](Rect) + /// + /// This is typically used when computing total padding. /// /// **NOTE:** this is *not* the width of the rectangle. pub(crate) fn horizontal(&self) -> T { self.start + self.end } - /// The sum of the coordinates of the bottom and top side of the rectangle + /// The sum of [`Rect.top`](Rect) and [`Rect.bottom](Rect) + /// + /// This is typically used when computing total padding. /// /// **NOTE:** this is *not* the height of the rectangle. pub(crate) fn vertical(&self) -> T { self.top + self.bottom } - /// The main layout-axis sum of the rectangle + /// The sum of the two fields of the [`Rect`] representing the main axis. /// - /// This is always perpendicular to [`Rect::cross`]. + /// This is typically used when computing total padding. /// /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::horizontal`]. /// Otherwise, this is [`Rect::vertical`]. @@ -82,9 +96,7 @@ where } } - /// The cross layout-axis sum of the rectangle - /// - /// This is always perpendicular to [`Rect::main`]. + /// The sum of the two fields of the [`Rect`] representing the cross axis. /// /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::vertical`]. /// Otherwise, this is [`Rect::horizontal`]. @@ -101,7 +113,7 @@ impl Rect where T: Copy + Clone, { - /// The lowest coordinate of the rectangle, from the perspective of the main layout axis + /// 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 { if direction.is_row() { self.start @@ -110,7 +122,7 @@ where } } - /// The highest coordinate of the rectangle, from the perspective of the main layout axis + /// 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 { if direction.is_row() { self.end @@ -119,7 +131,7 @@ where } } - /// The lowest coordinate of the rectangle, from the perspective of the cross layout axis + /// 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 { if direction.is_row() { self.top @@ -128,7 +140,7 @@ where } } - /// The highest coordinate of the rectangle, from the perspective of the cross layout axis + /// 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 { if direction.is_row() { self.bottom From ac1a88cf874112a6283a16a01e9634d5c14027bf Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 12:25:03 -0400 Subject: [PATCH 14/35] Typo fix Co-authored-by: TimJentzsch --- src/node.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node.rs b/src/node.rs index 345551cef..df33a7b94 100644 --- a/src/node.rs +++ b/src/node.rs @@ -14,7 +14,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; /// A function that can be applied to a `Size` to obetain a `` pub enum MeasureFunc { - /// Stores a unboxed function + /// Stores an unboxed function Raw(fn(Size) -> Size), /// Stores a boxed function #[cfg(any(feature = "std", feature = "alloc"))] From f3339762f655557db8e59532e6af846079df56ba Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 12:25:15 -0400 Subject: [PATCH 15/35] Typo fix Co-authored-by: TimJentzsch --- src/node.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node.rs b/src/node.rs index df33a7b94..7055c63e0 100644 --- a/src/node.rs +++ b/src/node.rs @@ -12,7 +12,7 @@ use crate::sys::{new_map_with_capacity, ChildrenVec, Map, Vec}; use crate::Error; use core::sync::atomic::{AtomicUsize, Ordering}; -/// A function that can be applied to a `Size` to obetain a `` +/// A function that can be applied to a `Size` to obtain a `` pub enum MeasureFunc { /// Stores an unboxed function Raw(fn(Size) -> Size), From 59bea78aad5989b3ad1bddae8426c2d2ebb67916 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 12:26:10 -0400 Subject: [PATCH 16/35] Revert non-exhaustive annotation Co-authored-by: TimJentzsch --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index fe28594d0..a3a80f5d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,6 @@ pub use crate::node::Sprawl; use core::fmt::{Display, Formatter, Result}; /// An error that can occur when performing layout -#[non_exhaustive] #[derive(Debug)] pub enum Error { /// The [`Node`](node::Node) was invalid From 549ab65a5ddb71bc9482a6a12e95cea9e841a843 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 12:29:56 -0400 Subject: [PATCH 17/35] Rename unclear method names on Rect --- src/flexbox.rs | 63 ++++++++++++++++++++++++++----------------------- src/geometry.rs | 16 ++++++------- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/src/flexbox.rs b/src/flexbox.rs index e002e019d..79e50f965 100644 --- a/src/flexbox.rs +++ b/src/flexbox.rs @@ -196,8 +196,8 @@ impl Forest { }; let node_inner_size = Size { - width: node_size.width - padding_border.horizontal(), - height: node_size.height - padding_border.vertical(), + width: node_size.width - padding_border.horizontal_axis_sum(), + height: node_size.height - padding_border.vertical_axis_sum(), }; let container_size = Size::zero(); @@ -274,10 +274,10 @@ impl Forest { constants: &AlgoConstants, ) -> Size { Size { - width: node_size.width.or_else(parent_size.width - constants.margin.horizontal()) - - constants.padding_border.horizontal(), - height: node_size.height.or_else(parent_size.height - constants.margin.vertical()) - - constants.padding_border.vertical(), + width: node_size.width.or_else(parent_size.width - constants.margin.horizontal_axis_sum()) + - constants.padding_border.horizontal_axis_sum(), + height: node_size.height.or_else(parent_size.height - constants.margin.vertical_axis_sum()) + - constants.padding_border.vertical_axis_sum(), } } @@ -405,8 +405,9 @@ impl Forest { // used min and max main sizes (and flooring the content box size at zero). for child in flex_items { - child.inner_flex_basis = - child.flex_basis - child.padding.main(constants.dir) - child.border.main(constants.dir); + child.inner_flex_basis = child.flex_basis + - child.padding.main_axis_sum(constants.dir) + - child.border.main_axis_sum(constants.dir); // TODO - not really spec abiding but needs to be done somewhere. probably somewhere else though. // The following logic was developed not from the spec but by trail and error looking into how @@ -427,7 +428,7 @@ impl Forest { child.hypothetical_outer_size.set_main( constants.dir, - child.hypothetical_inner_size.main(constants.dir) + child.margin.main(constants.dir), + child.hypothetical_inner_size.main(constants.dir) + child.margin.main_axis_sum(constants.dir), ); } } @@ -544,9 +545,10 @@ impl Forest { // TODO this should really only be set inside the if-statement below but // that causes the target_main_size to never be set for some items - child - .outer_target_size - .set_main(constants.dir, child.target_size.main(constants.dir) + child.margin.main(constants.dir)); + child.outer_target_size.set_main( + constants.dir, + child.target_size.main(constants.dir) + child.margin.main_axis_sum(constants.dir), + ); let child_style = &self.nodes[child.node].style; if (child_style.flex_grow == 0.0 && child_style.flex_shrink == 0.0) @@ -565,7 +567,7 @@ impl Forest { .items .iter() .map(|child| { - child.margin.main(constants.dir) + child.margin.main_axis_sum(constants.dir) + if child.frozen { child.target_size.main(constants.dir) } else { child.flex_basis } }) .sum(); @@ -592,7 +594,7 @@ impl Forest { .items .iter() .map(|child| { - child.margin.main(constants.dir) + child.margin.main_axis_sum(constants.dir) + if child.frozen { child.target_size.main(constants.dir) } else { child.flex_basis } }) .sum(); @@ -687,9 +689,10 @@ impl Forest { let clamped = child.target_size.main(constants.dir).maybe_min(max_main).maybe_max(min_main).max(0.0); child.violation = clamped - child.target_size.main(constants.dir); child.target_size.set_main(constants.dir, clamped); - child - .outer_target_size - .set_main(constants.dir, child.target_size.main(constants.dir) + child.margin.main(constants.dir)); + child.outer_target_size.set_main( + constants.dir, + child.target_size.main(constants.dir) + child.margin.main_axis_sum(constants.dir), + ); acc + child.violation }); @@ -766,7 +769,7 @@ impl Forest { child.hypothetical_outer_size.set_cross( constants.dir, - child.hypothetical_inner_size.cross(constants.dir) + child.margin.cross(constants.dir), + child.hypothetical_inner_size.cross(constants.dir) + child.margin.cross_axis_sum(constants.dir), ); } } @@ -860,7 +863,7 @@ impl Forest { ) { if flex_lines.len() == 1 && node_size.cross(constants.dir).is_defined() { flex_lines[0].cross_size = - (node_size.cross(constants.dir) - constants.padding_border.cross(constants.dir)).or_else(0.0); + (node_size.cross(constants.dir) - constants.padding_border.cross_axis_sum(constants.dir)).or_else(0.0); } else { for line in flex_lines.iter_mut() { // 1. Collect all the flex items whose inline-axis is parallel to the main-axis, whose @@ -915,7 +918,7 @@ impl Forest { { let total_cross: f32 = flex_lines.iter().map(|line| line.cross_size).sum(); let inner_cross = - (node_size.cross(constants.dir) - constants.padding_border.cross(constants.dir)).or_else(0.0); + (node_size.cross(constants.dir) - constants.padding_border.cross_axis_sum(constants.dir)).or_else(0.0); if total_cross < inner_cross { let remaining = inner_cross - total_cross; @@ -950,7 +953,7 @@ impl Forest { && child_style.cross_margin_end(constants.dir) != Dimension::Auto && child_style.cross_size(constants.dir) == Dimension::Auto { - (line_cross_size - child.margin.cross(constants.dir)) + (line_cross_size - child.margin.cross_axis_sum(constants.dir)) .maybe_max(child.min_size.cross(constants.dir)) .maybe_min(child.max_size.cross(constants.dir)) } else { @@ -960,7 +963,7 @@ impl Forest { child.outer_target_size.set_cross( constants.dir, - child.target_size.cross(constants.dir) + child.margin.cross(constants.dir), + child.target_size.cross(constants.dir) + child.margin.cross_axis_sum(constants.dir), ); } } @@ -1213,12 +1216,14 @@ impl Forest { constants.container_size.set_cross( constants.dir, - node_size.cross(constants.dir).or_else(total_cross_size + constants.padding_border.cross(constants.dir)), + node_size + .cross(constants.dir) + .or_else(total_cross_size + constants.padding_border.cross_axis_sum(constants.dir)), ); constants.inner_container_size.set_cross( constants.dir, - constants.container_size.cross(constants.dir) - constants.padding_border.cross(constants.dir), + constants.container_size.cross(constants.dir) - constants.padding_border.cross_axis_sum(constants.dir), ); total_cross_size @@ -1331,7 +1336,7 @@ impl Forest { }; total_offset_main += - child.offset_main + child.margin.main(constants.dir) + result.size.main(constants.dir); + child.offset_main + child.margin.main_axis_sum(constants.dir) + result.size.main(constants.dir); }; if constants.dir.is_reverse() { @@ -1534,8 +1539,8 @@ impl Forest { return ComputeResult { size: Size { - width: node_size.width.or_else(0.0) + constants.padding_border.horizontal(), - height: node_size.height.or_else(0.0) + constants.padding_border.vertical(), + width: node_size.width.or_else(0.0) + constants.padding_border.horizontal_axis_sum(), + height: node_size.height.or_else(0.0) + constants.padding_border.vertical_axis_sum(), }, }; } @@ -1580,7 +1585,7 @@ impl Forest { acc.max(length) }); - let size = longest_line + constants.padding_border.main(constants.dir); + let size = longest_line + constants.padding_border.main_axis_sum(constants.dir); match available_space.main(constants.dir) { Defined(val) if flex_lines.len() > 1 && size < val => val, _ => size, @@ -1590,7 +1595,7 @@ impl Forest { constants.inner_container_size.set_main( constants.dir, - constants.container_size.main(constants.dir) - constants.padding_border.main(constants.dir), + constants.container_size.main(constants.dir) - constants.padding_border.main_axis_sum(constants.dir), ); // 9.4. Cross Size Determination diff --git a/src/geometry.rs b/src/geometry.rs index ec1f66f66..65bf74f23 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -69,7 +69,7 @@ where /// This is typically used when computing total padding. /// /// **NOTE:** this is *not* the width of the rectangle. - pub(crate) fn horizontal(&self) -> T { + pub(crate) fn horizontal_axis_sum(&self) -> T { self.start + self.end } @@ -78,7 +78,7 @@ where /// This is typically used when computing total padding. /// /// **NOTE:** this is *not* the height of the rectangle. - pub(crate) fn vertical(&self) -> T { + pub(crate) fn vertical_axis_sum(&self) -> T { self.top + self.bottom } @@ -88,11 +88,11 @@ where /// /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::horizontal`]. /// Otherwise, this is [`Rect::vertical`]. - pub(crate) fn main(&self, direction: FlexDirection) -> T { + pub(crate) fn main_axis_sum(&self, direction: FlexDirection) -> T { if direction.is_row() { - self.horizontal() + self.horizontal_axis_sum() } else { - self.vertical() + self.vertical_axis_sum() } } @@ -100,11 +100,11 @@ where /// /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::vertical`]. /// Otherwise, this is [`Rect::horizontal`]. - pub(crate) fn cross(&self, direction: FlexDirection) -> T { + pub(crate) fn cross_axis_sum(&self, direction: FlexDirection) -> T { if direction.is_row() { - self.vertical() + self.vertical_axis_sum() } else { - self.horizontal() + self.horizontal_axis_sum() } } } From af8243ec602165fbfc854cb8f3b38523459cd4e2 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 12:30:55 -0400 Subject: [PATCH 18/35] Clarify wording of mark_dirty field Co-authored-by: TimJentzsch --- src/node.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node.rs b/src/node.rs index 7055c63e0..b3f93251e 100644 --- a/src/node.rs +++ b/src/node.rs @@ -257,7 +257,7 @@ impl Sprawl { Ok(()) } - /// Does the layout of this node (and its children) need to be recomputed + /// Indicates whether the layout of this node (and its children) need to be recomputed pub fn dirty(&self, node: Node) -> Result { let id = self.find_node(node)?; Ok(self.forest.nodes[id].is_dirty) From bb48a23f405b6511527cc827666af2c41ae7b6a5 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 12:31:13 -0400 Subject: [PATCH 19/35] Clarify wording of mark_dirty field Co-authored-by: TimJentzsch --- src/node.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node.rs b/src/node.rs index b3f93251e..0ff0a1be3 100644 --- a/src/node.rs +++ b/src/node.rs @@ -250,7 +250,7 @@ impl Sprawl { Ok(&self.forest.nodes[id].layout) } - /// Indicates that the layout of this node and its children must be recomputed + /// Marks the layout computation of this node and its children as outdated pub fn mark_dirty(&mut self, node: Node) -> Result<(), Error> { let id = self.find_node(node)?; self.forest.mark_dirty(id); From acbc51104b4ba95d2973cf1aa836d92b457316ac Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 12:34:00 -0400 Subject: [PATCH 20/35] Use `parent` as the argument name over `node` in methods on `Forest` --- src/forest.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/forest.rs b/src/forest.rs index 9d3655ea1..adce8e613 100644 --- a/src/forest.rs +++ b/src/forest.rs @@ -85,11 +85,11 @@ impl Forest { id } - /// Adds a `child` node to the parent `node` - pub(crate) fn add_child(&mut self, node: NodeId, child: NodeId) { - self.parents[child].push(node); - self.children[node].push(child); - self.mark_dirty(node) + /// Adds a `child` node to the `parent` node + pub(crate) fn add_child(&mut self, parent: NodeId, child: NodeId) { + self.parents[child].push(parent); + self.children[parent].push(child); + self.mark_dirty(parent) } /// Removes all nodes and resets the data structure @@ -173,21 +173,21 @@ impl Forest { } } - /// Breaks the link between the parent `node` and the `child` node + /// Breaks the link between the `parent` node and the `child` node /// /// The `child`'s data is not removed. - pub(crate) fn remove_child(&mut self, node: NodeId, child: NodeId) -> NodeId { - let index = self.children[node].iter().position(|n| *n == child).unwrap(); - self.remove_child_at_index(node, index) + pub(crate) fn remove_child(&mut self, parent: NodeId, child: NodeId) -> NodeId { + let index = self.children[parent].iter().position(|n| *n == child).unwrap(); + self.remove_child_at_index(parent, index) } - /// Breaks the link between the parent `node` and the n-th child node + /// Breaks the link between the `parent` node and the n-th child node /// /// The child's data is not removed. - pub(crate) fn remove_child_at_index(&mut self, node: NodeId, index: usize) -> NodeId { - let child = self.children[node].remove(index); - self.parents[child].retain(|p| *p != node); - self.mark_dirty(node); + pub(crate) fn remove_child_at_index(&mut self, parent: NodeId, index: usize) -> NodeId { + let child = self.children[parent].remove(index); + self.parents[child].retain(|p| *p != parent); + self.mark_dirty(parent); child } From 467c465a652722eea0aadf2ed0801aa79489b40d Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 12:35:05 -0400 Subject: [PATCH 21/35] index -> child_index --- src/forest.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/forest.rs b/src/forest.rs index adce8e613..62073fb06 100644 --- a/src/forest.rs +++ b/src/forest.rs @@ -184,8 +184,8 @@ impl Forest { /// Breaks the link between the `parent` node and the n-th child node /// /// The child's data is not removed. - pub(crate) fn remove_child_at_index(&mut self, parent: NodeId, index: usize) -> NodeId { - let child = self.children[parent].remove(index); + pub(crate) fn remove_child_at_index(&mut self, parent: NodeId, child_index: usize) -> NodeId { + let child = self.children[parent].remove(child_index); self.parents[child].retain(|p| *p != parent); self.mark_dirty(parent); child From 2ea5bad335dbecac3bdee2d39a71f3b5420aaaaa Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 12:41:29 -0400 Subject: [PATCH 22/35] Fix incorrect comment --- src/style.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/style.rs b/src/style.rs index 9ffefccac..11877bd8e 100644 --- a/src/style.rs +++ b/src/style.rs @@ -269,7 +269,6 @@ pub struct Style { pub justify_content: JustifyContent, pub position: Rect, pub margin: Rect, - /// WARNING: this currently has no effect! pub padding: Rect, pub border: Rect, pub flex_grow: f32, From 806de4c3da8185f20f65f88a1c39b8bec3a44ebc Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 12:58:06 -0400 Subject: [PATCH 23/35] More docs for style.rs --- src/style.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/style.rs b/src/style.rs index 11877bd8e..95a3179b5 100644 --- a/src/style.rs +++ b/src/style.rs @@ -27,7 +27,9 @@ impl Default for AlignItems { } } -/// Allows the alignment specified by [`AlignItems`] to be overridden by child [`Nodes`](crate::node::Node). +/// Overrides the inherited [`AlignItems`] behavior for this node. +/// +/// The behavior of any child nodes will be controlled by this node's [`AlignItems`] value. /// /// The default behavior is [`AlignSelf::Auto`]. #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -43,7 +45,7 @@ pub enum AlignSelf { Center, /// Items are aligned such as their baselines align Baseline, - /// Stretch to fill the container + /// Distribute items evenly, but stretch them to fill the container Stretch, } @@ -53,14 +55,24 @@ impl Default for AlignSelf { } } +/// Sets the distribution of space between and around content items along the cross-axis +/// +/// The default value is [`AlignContent::Stretch`]. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum AlignContent { + /// Items are packed toward the start of the cross axis FlexStart, + /// Items are packed toward the end of the cross axis FlexEnd, + /// Items are packed along the center of the cross axis Center, + /// Distribute items evenly, but stretch them to fill the container Stretch, + /// Distribute items evenly, such that the first and last item are aligned with the edges SpaceBetween, + /// Distribute items evenly, + /// such that the space between items is the same as the space between the first and last item and the edges SpaceAround, } @@ -87,11 +99,16 @@ impl Default for Direction { } } +/// Sets the layout used for the children of this node +/// +/// [`Display::Flex`] is the default value. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Display { + /// The children will follow the flexbox layout algorithm #[cfg_attr(feature = "serde", serde(rename = "flex"))] Flex, + /// The children will not be laid out, and will follow absolute positioning #[cfg_attr(feature = "serde", serde(rename = "none"))] None, } @@ -102,10 +119,11 @@ impl Default for Display { } } -/// The direction in which the flexbox layout should be defined relative to. +/// The direction of the flexbox layout main axis. /// /// There are always two perpendicular layout axes: main (or primary) and cross (or secondary). -/// Items will be laid out along the main axis (which can then be swapped for the children). +/// Adding items will cause them to be positioned adjacent to each other along the main axis. +/// By varying this value throughout your tree, you can create complex axis-aligned layouts. /// /// Items are always aligned relative to the cross axis, and justified relative to the main axis. /// @@ -253,6 +271,10 @@ impl Default for Size { } } +/// The flexbox layout information for a single [`Node`](crate::node::Node). +/// +/// This struct follows the [CSS equivalent](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) directly; +/// information about the behavior on the web should transfer directly. #[derive(Copy, Clone, PartialEq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(default))] From 545330f4961be110ec98f68e5f6a10eb224903e8 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 13:47:35 -0400 Subject: [PATCH 24/35] Fix merge error --- src/style.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/style.rs b/src/style.rs index 6e9a21a11..af89c6285 100644 --- a/src/style.rs +++ b/src/style.rs @@ -82,8 +82,6 @@ impl Default for AlignContent { } } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] /// Sets the layout used for the children of this node /// /// [`Display::Flex`] is the default value. From 98a7aed3635c503d8bf63161d13323319c685860 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 13:53:49 -0400 Subject: [PATCH 25/35] Document JustifyContent --- src/style.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/style.rs b/src/style.rs index af89c6285..cd6f1d814 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,4 +1,4 @@ -//! A representation of the [CSS style attribute](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) in Rust, used for flexbox layout +//! A representation of [CSS layout properties](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) in Rust, used for flexbox layout use crate::geometry::{Rect, Size}; use crate::number::Number; @@ -155,14 +155,25 @@ impl FlexDirection { } } +/// Sets the distribution of space between and around content items along the main-axis +/// +/// The default value is [`JustifyContent::FlexStart`]. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum JustifyContent { + /// Items are packed toward the start of the main axis FlexStart, + /// Items are packed toward the end of the main axis FlexEnd, + /// Items are packed along the center of the main axis Center, + /// Distribute items evenly, such that the first and last item are aligned with the edges SpaceBetween, + /// Distribute items evenly, + /// such that the space between items is twice same as the space between the first and last item and the edges SpaceAround, + /// Distribute items evenly, + /// such that the space between items is the same as the space between the first and last item and the edges SpaceEvenly, } From ba44c89d9845434c8af8a7052cf834994b4b7c1d Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 15:19:34 -0400 Subject: [PATCH 26/35] Finish documenting style.rs --- src/style.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/style.rs b/src/style.rs index 2e962dbfd..2b89a986a 100644 --- a/src/style.rs +++ b/src/style.rs @@ -183,10 +183,26 @@ impl Default for JustifyContent { } } +/// The positioning strategy for this item. +/// +/// This controls both how the origin is determined for the [`Style::position`] field, +/// and whether or not the item will be controlled by flexbox's layout algorithm. +/// +/// WARNING: this enum follows the behavior of [CSS's `position` property](https://developer.mozilla.org/en-US/docs/Web/CSS/position), +/// which can be unintuitive. +/// +/// [`PositionType::Relative`] is the default value, in contrast to the default behavior in CSS. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum PositionType { + /// The offset is computed relative to the final position given by the layout algorithm. + /// Offsets do not affect the position of any other items; they are effectively a correction factor applied at the end. Relative, + /// The offset is computed relative to this item's closest positioned ancestor, if any. + /// Otherwise, it is placed relative to the origin. + /// No space is created for the item in the page layout, and its size will not be altered. + /// + /// WARNING: to opt-out of layouting entirely, you must use [`Display::None`] instead on your [`Style`] object. Absolute, } @@ -196,11 +212,15 @@ impl Default for PositionType { } } +/// Controls whether flex items are forced onto one line or can wrap onto multiple lines. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum FlexWrap { + /// Items will not wrap NoWrap, + /// Items will wrap according to this item's [`FlexDirection`] Wrap, + /// Items will wrap in the oppposite direction to this item's [`FlexDirection`] WrapReverse, } @@ -210,12 +230,22 @@ 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`]. #[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)) + /// + /// Each point is about 0.353 mm in size. Points(f32), + /// The dimension is stored in percentage relative to the parent item. Percent(f32), } @@ -226,6 +256,7 @@ impl Default for Dimension { } impl Dimension { + /// Converts the given [`Dimension`] into a concrete value of points pub(crate) fn resolve(self, parent_dim: Number) -> Number { match self { Dimension::Points(points) => Number::Defined(points), @@ -234,6 +265,7 @@ impl Dimension { } } + /// Is this value defined? pub(crate) fn is_defined(self) -> bool { matches!(self, Dimension::Points(_) | Dimension::Percent(_)) } @@ -253,30 +285,66 @@ impl Default for Size { /// The flexbox layout information for a single [`Node`](crate::node::Node). /// +/// The most important idea in flexbox is the notion of a "main" and "cross" axis, which are always perpendicular to each other. +/// The orientation of these axes are controlled via the [`FlexDirection`] field of this struct. +/// /// This struct follows the [CSS equivalent](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) directly; /// information about the behavior on the web should transfer directly. +/// +/// Detailed information about the exact behavior of each of these fields +/// can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS) by searching for the field name. +/// The distinction between margin, padding and border is explained well in +/// this [introduction to the box model](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model). +/// +/// If the behavior does not match the flexbox layout algorithm on the web, please file a bug! #[derive(Copy, Clone, PartialEq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(default))] pub struct Style { + /// What layout strategy should be used? pub display: Display, + /// What should the `position` value of this struct use as a base offset? pub position_type: PositionType, + /// Which direction does the main axis flow in? pub flex_direction: FlexDirection, + /// Should elements wrap, or stay in a single line? pub flex_wrap: FlexWrap, + /// How should items be aligned relative to the cross axis? pub align_items: AlignItems, + /// Should this item violate the cross axis alignment specified by its parent's [`AlignItems`]? pub align_self: AlignSelf, + /// How should content contained within this item be aligned relative to the cross axis? pub align_content: AlignContent, + /// How should items be aligned relative to the main axis? pub justify_content: JustifyContent, + /// How should the position of this element be tweaked relative to the layout defined? pub position: Rect, + /// How large should the margin be on each side? pub margin: Rect, + /// How large should the padding be on each side? pub padding: Rect, + /// How large should the border be on each side? pub border: Rect, + /// The relative rate at which this item grows when it is expanding to fill space + /// + /// 1.0 is the default value, and this value must be positive. pub flex_grow: f32, + /// The relative rate at which this item shrinks when it is contracting to fit into space + /// + /// 1.0 is the default value, and this value must be positive. pub flex_shrink: f32, + /// Sets the initial main axis size of the item pub flex_basis: Dimension, + /// Sets the initial size of the item + // TODO: why does this exist as distinct from flex_basis? How do they interact? pub size: Size, + /// Controls the minimum size of the item pub min_size: Size, + /// Controls the maximum size of the item pub max_size: Size, + /// Sets the preferred aspect ratio for the item + /// + /// The ratio is calculated as width divided by height. pub aspect_ratio: Number, } From b870891ec0d0faca8324aedf41b8cbf22bd1093b Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 16:25:13 -0400 Subject: [PATCH 27/35] Small improvements to style.rs docs Co-authored-by: Andreas Weibye <13300393+Weibye@users.noreply.github.com> --- src/style.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/style.rs b/src/style.rs index 2b89a986a..762c6b643 100644 --- a/src/style.rs +++ b/src/style.rs @@ -6,6 +6,8 @@ use crate::number::Number; /// How [`Nodes`](crate::node::Node) are aligned relative to the cross axis /// /// The default behavior is [`AlignItems::Stretch`]. +/// +/// [Specification](https://www.w3.org/TR/css-flexbox-1/#align-items-property) #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum AlignItems { @@ -32,6 +34,8 @@ impl Default for AlignItems { /// The behavior of any child nodes will be controlled by this node's [`AlignItems`] value. /// /// The default behavior is [`AlignSelf::Auto`]. +/// +/// [Specification](https://www.w3.org/TR/css-flexbox-1/#align-items-property) #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum AlignSelf { @@ -58,6 +62,8 @@ impl Default for AlignSelf { /// Sets the distribution of space between and around content items along the cross-axis /// /// The default value is [`AlignContent::Stretch`]. +/// +/// [Specification](https://www.w3.org/TR/css-flexbox-1/#align-content-property) #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum AlignContent { @@ -111,6 +117,8 @@ impl Default for Display { /// Items are always aligned relative to the cross axis, and justified relative to the main axis. /// /// The default behavior is [`FlexDirection::Row`]. +/// +/// [Specification](https://www.w3.org/TR/css-flexbox-1/#flex-direction-property) #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum FlexDirection { @@ -158,6 +166,8 @@ impl FlexDirection { /// Sets the distribution of space between and around content items along the main-axis /// /// The default value is [`JustifyContent::FlexStart`]. +/// +/// [Specification](https://www.w3.org/TR/css-flexbox-1/#justify-content-property) #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum JustifyContent { @@ -213,10 +223,14 @@ impl Default for PositionType { } /// Controls whether flex items are forced onto one line or can wrap onto multiple lines. +/// +/// Defaults to [`FlexWrap::NoWrap`] +/// +/// [Specification](https://www.w3.org/TR/css-flexbox-1/#flex-wrap-property) #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum FlexWrap { - /// Items will not wrap + /// Items will not wrap and stay on a single line NoWrap, /// Items will wrap according to this item's [`FlexDirection`] Wrap, From 3a33105a0e02dda67cbc09bd2803b435aa688545 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 16:26:00 -0400 Subject: [PATCH 28/35] Typo fix Co-authored-by: Andreas Weibye <13300393+Weibye@users.noreply.github.com> --- src/node.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node.rs b/src/node.rs index 0ff0a1be3..b6f7cd3ff 100644 --- a/src/node.rs +++ b/src/node.rs @@ -79,7 +79,7 @@ impl Sprawl { Node { instance: self.id, local } } - /// Stores a new + /// Stores a new node in the tree fn add_node(&mut self, node: Node, id: NodeId) { let _ = self.nodes_to_ids.insert(node, id); let _ = self.ids_to_nodes.insert(id, node); From 6806b433f3fd48536d04b27d34cf40a278693624 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 16:50:49 -0400 Subject: [PATCH 29/35] Document sys.rs --- src/sys.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/sys.rs b/src/sys.rs index 5dc0000ba..0d5c51c2d 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -14,12 +14,18 @@ pub use self::core::*; #[cfg(feature = "std")] mod std { + /// An allocation-backend agnostic [`Box`] type pub type Box = std::boxed::Box; + /// An allocation-backend agnostic map type pub type Map = std::collections::HashMap; + /// An allocation-backend agnostic vector type pub type Vec = std::vec::Vec; + /// A vector of child nodes pub type ChildrenVec = std::vec::Vec; + /// A vector of parent nodes pub type ParentsVec = std::vec::Vec; + /// Creates a new map with the capacity for the specified number of items before it must be resized pub fn new_map_with_capacity(capacity: usize) -> Map where K: Eq + std::hash::Hash, @@ -27,16 +33,19 @@ mod std { Map::with_capacity(capacity) } + /// Creates a new vector with the capacity for the specified number of items before it must be resized pub fn new_vec_with_capacity(capacity: usize) -> Vec { Vec::with_capacity(capacity) } #[inline] + /// Rounds to the nearest whole number pub fn round(value: f32) -> f32 { value.round() } #[inline] + /// Computes the absolute value pub fn abs(value: f32) -> f32 { value.abs() } @@ -46,26 +55,35 @@ mod std { mod alloc { extern crate alloc; + /// An allocation-backend agnostic `Box` type pub type Box = alloc::boxed::Box; + /// An allocation-backend agnostic map type pub type Map = hashbrown::HashMap; + /// An allocation-backend agnostic vector type pub type Vec = alloc::vec::Vec; + /// A vector of child nodes pub type ChildrenVec = alloc::vec::Vec; + /// A vector of parent nodes pub type ParentsVec = alloc::vec::Vec; + /// Creates a new map with the capacity for the specified number of items before it must be resized pub fn new_map_with_capacity(capacity: usize) -> Map { Map::with_capacity(capacity) } + /// Creates a new vector with the capacity for the specified number of items before it must be resized pub fn new_vec_with_capacity(capacity: usize) -> Vec { Vec::with_capacity(capacity) } #[inline] + /// Rounds to the nearest whole number pub fn round(value: f32) -> f32 { num_traits::float::FloatCore::round(value) } #[inline] + /// Computes the absolute value pub fn abs(value: f32) -> f32 { num_traits::float::FloatCore::abs(value) } @@ -73,15 +91,25 @@ mod alloc { #[cfg(all(not(feature = "alloc"), not(feature = "std")))] mod core { + /// The maximum number of nodes in the forest pub const MAX_NODE_COUNT: usize = 256; + /// The maximum number of children of any given node pub const MAX_CHILD_COUNT: usize = 16; + /// The maximum number of parents for each child pub const MAX_PARENTS_COUNT: usize = 1; + /// An allocation-backend agnostic map type pub type Map = crate::indexmap::FnvIndexMap; + /// An allocation-backend agnostic vector type pub type Vec = arrayvec::ArrayVec; + /// A vector of child nodes, whose length cannot exceed [`MAX_CHILD_COUNT`] pub type ChildrenVec = arrayvec::ArrayVec; + /// A vector of parent nodes, whose length cannot exceed [`MAX_PARENTS_COUNT`] pub type ParentsVec = arrayvec::ArrayVec; + /// Creates a new map with the capacity for the specified number of items + /// + /// This map cannot be resized. pub fn new_map_with_capacity(_capacity: usize) -> Map where K: Eq + ::hash32::Hash, @@ -89,15 +117,20 @@ mod core { Map::new() } + /// Creates a new map with the capacity for the specified number of items before it must be resized + /// + /// This vector cannot be resized. pub fn new_vec_with_capacity(_capacity: usize) -> arrayvec::ArrayVec { arrayvec::ArrayVec::new() } + /// Rounds to the nearest whole number #[inline] pub fn round(value: f32) -> f32 { num_traits::float::FloatCore::round(value) } + /// Computes the absolute value #[inline] pub fn abs(value: f32) -> f32 { num_traits::float::FloatCore::abs(value) From 7e53765ae5685fdf58713d18eb5be3b99e8d7aa8 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 16:56:29 -0400 Subject: [PATCH 30/35] Document more of forest.rs --- src/forest.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/forest.rs b/src/forest.rs index d5aeca788..eb279798a 100644 --- a/src/forest.rs +++ b/src/forest.rs @@ -12,11 +12,16 @@ use crate::sys::{new_vec_with_capacity, ChildrenVec, ParentsVec, Vec}; /// /// Stored in a [`Forest`]. pub(crate) struct NodeData { + /// The layout strategy used by this node pub(crate) style: Style, + /// The mapping from the Size (in units) to Size (in points) for this node pub(crate) measure: Option, + /// The results of the layout computation pub(crate) layout: Layout, + /// The cached results of the layout computation pub(crate) main_size_layout_cache: Option, pub(crate) other_layout_cache: Option, + /// Does this node's layout need to be recomputed? pub(crate) is_dirty: bool, } @@ -59,8 +64,15 @@ impl NodeData { /// A collection of UI layout trees used to store [`NodeData`] associated with specific [`Nodes`](crate::node::Node) pub(crate) struct Forest { + /// The [`NodeData`] for each node stored in this forest pub(crate) nodes: Vec, + /// The children of each node + /// + /// The indexes in the outer vector correspond to the position of the parent [`NodeData`] pub(crate) children: Vec>, + /// The parents of each node + /// + /// The indexes in the outer vector correspond to the position of the child [`NodeData`] pub(crate) parents: Vec>, } @@ -205,8 +217,9 @@ impl Forest { /// /// Any cached layout information is cleared. pub(crate) fn mark_dirty(&mut self, node: NodeId) { - // Performs a recursive depth-first search up the tree until the root node is reached - // WARNING: this will stack-overflow if the tree contains a cycle + /// Performs a recursive depth-first search up the tree until the root node is reached + /// + /// WARNING: this will stack-overflow if the tree contains a cycle fn mark_dirty_recursive(nodes: &mut Vec, parents: &[ParentsVec], node_id: NodeId) { nodes[node_id].mark_dirty(); From 8b2335dbe25b5af40478feee88d971facfce191d Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 21:00:26 -0400 Subject: [PATCH 31/35] Docs tweaks Co-authored-by: TimJentzsch --- src/node.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node.rs b/src/node.rs index b6f7cd3ff..a405021a4 100644 --- a/src/node.rs +++ b/src/node.rs @@ -12,7 +12,7 @@ use crate::sys::{new_map_with_capacity, ChildrenVec, Map, Vec}; use crate::Error; use core::sync::atomic::{AtomicUsize, Ordering}; -/// A function that can be applied to a `Size` to obtain a `` +/// A function that can be applied to a `Size` to obtain a `Size` pub enum MeasureFunc { /// Stores an unboxed function Raw(fn(Size) -> Size), @@ -122,7 +122,7 @@ impl Sprawl { /// Remove a specific [`Node`] from the tree /// - /// Its [`Id`] is rendered invalid + /// Its [`Id`] is marked as invalid. pub fn remove(&mut self, node: Node) { let id = if let Ok(id) = self.find_node(node) { id } else { return }; @@ -184,7 +184,7 @@ impl Sprawl { Ok(self.ids_to_nodes[&prev_id]) } - /// Removes the "n-th" child from the parent `node` + /// Removes the child at the given `index` from the parent `node` /// /// The child is not removed from the forest entirely, it is simply no longer attached to its previous parent. pub fn remove_child_at_index(&mut self, node: Node, index: usize) -> Result { @@ -195,7 +195,7 @@ impl Sprawl { Ok(self.ids_to_nodes[&prev_id]) } - /// Replaces the "n-th" child from the parent `node` with the new `child` node + /// Replaces the child at the given `index` from the parent `node` with the new `child` node /// /// The child is not removed from the forest entirely, it is simply no longer attached to its previous parent. pub fn replace_child_at_index(&mut self, node: Node, index: usize, child: Node) -> Result { From ec7a46059e89eb628e444b245615282372595d15 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 21:01:21 -0400 Subject: [PATCH 32/35] Typos Co-authored-by: TimJentzsch --- src/geometry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/geometry.rs b/src/geometry.rs index 65bf74f23..f69abddaf 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -64,7 +64,7 @@ impl Rect where T: Add + Copy + Clone, { - /// The sum of [`Rect.left`](Rect) and [`Rect.right](Rect) + /// The sum of [`Rect.start`](Rect) and [`Rect.end`](Rect) /// /// This is typically used when computing total padding. /// @@ -73,7 +73,7 @@ where self.start + self.end } - /// The sum of [`Rect.top`](Rect) and [`Rect.bottom](Rect) + /// The sum of [`Rect.top`](Rect) and [`Rect.bottom`](Rect) /// /// This is typically used when computing total padding. /// From 410c807fc9d2718a4b64b2f60933c7822c057911 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 9 Jun 2022 21:12:43 -0400 Subject: [PATCH 33/35] Caching notes --- src/forest.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/forest.rs b/src/forest.rs index eb279798a..f936b048b 100644 --- a/src/forest.rs +++ b/src/forest.rs @@ -18,8 +18,9 @@ pub(crate) struct NodeData { pub(crate) measure: Option, /// The results of the layout computation pub(crate) layout: Layout, - /// The cached results of the layout computation + /// The primary cached results of the layout computation pub(crate) main_size_layout_cache: Option, + /// Secondary cached results of the layout computation pub(crate) other_layout_cache: Option, /// Does this node's layout need to be recomputed? pub(crate) is_dirty: bool, From dba84c8c79124188582cb4182fdfb44f41227dd7 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Fri, 10 Jun 2022 12:46:35 -0400 Subject: [PATCH 34/35] Complete internal documentation --- src/flexbox.rs | 81 +++++++++++++++++++++++++++++++++++++++---------- src/geometry.rs | 1 + src/node.rs | 9 +++--- src/style.rs | 27 ++++++++++++----- src/sys.rs | 3 ++ 5 files changed, 94 insertions(+), 27 deletions(-) diff --git a/src/flexbox.rs b/src/flexbox.rs index dca7c7605..14d196b93 100644 --- a/src/flexbox.rs +++ b/src/flexbox.rs @@ -1,3 +1,6 @@ +//! Computes the [flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) layout algorithm on a [`Forest`](crate::forest::Forest) according to the [spec](https://www.w3.org/TR/css-flexbox-1/) +//! +//! Note that some minor steps appear to be missing: see https://github.com/DioxusLabs/sprawl/issues for more information. use core::f32; use crate::flexbox::Number::{Defined, Undefined}; @@ -11,65 +14,104 @@ use crate::style::{AlignContent, AlignSelf, Dimension, Display, FlexWrap, Justif use crate::style::{FlexDirection, Style}; use crate::sys::{abs, round, ChildrenVec, Vec}; +/// The computed results of a [`flexbox`](crate::flexbox) calculation #[derive(Debug, Clone)] pub struct ComputeResult { + /// The computed size pub size: Size, } +/// The intermediate results of a flexbox calculation for a single item struct FlexItem { + /// The identifier for the associated [`Node`](crate::node::Node) node: NodeId, + /// The base size of this item size: Size, + /// The minimum allowable size of this item min_size: Size, + /// The maximum allowable size of this item max_size: Size, + /// The final offset of this item position: Rect, + /// The margin of this item margin: Rect, + /// The padding of this item padding: Rect, + /// The border of this item border: Rect, + /// The default size of this item flex_basis: f32, + /// The default size of this item, minus padding and border inner_flex_basis: f32, + /// The amount by which this item has deviated from its target size violation: f32, + /// Is the size of this item locked frozen: bool, + /// The proposed inner size of this item hypothetical_inner_size: Size, + /// The proposed outer size of this item hypothetical_outer_size: Size, + /// The size that this item wants to be target_size: Size, + /// The size that this item wants to be, plus any padding and border outer_target_size: Size, + /// The position of the bottom edge of this item baseline: f32, - // temporary values for holding offset in the main / cross direction. - // offset is the relative position from the item's natural flow position based on - // relative position values, alignment, and justification. Does not include margin/padding/border. + /// A temporary values for holding offset in the main / cross direction. + + /// A temporary value for the main offset + /// + /// Offset is the relative position from the item's natural flow position based on + /// relative position values, alignment, and justification. Does not include margin/padding/border. offset_main: f32, + /// A temporary value for the cross offset offset_cross: f32, } +/// A line of [`FlexItem`] used for intermediate computation struct FlexLine<'a> { + /// The slice of items to items: &'a mut [FlexItem], + /// The dimensions of the cross-axis cross_size: f32, + /// The relative offset of the cross-axis offset_cross: f32, } -/// Constant values that can be reused for the flexbox algorithm. +/// Values that can be cached during the flexbox algorithm struct AlgoConstants { + /// The direction of the current segment being layed out dir: FlexDirection, + /// Is this segment a row is_row: bool, + /// Is this segment a column is_column: bool, + /// Is the wrap direction inverted is_wrap_reverse: bool, + /// The margin of this section margin: Rect, + /// The border of this section border: Rect, + /// The padding of this section padding_border: Rect, + /// The size of the internal node node_inner_size: Size, + /// The size of the surrounding container container_size: Size, + /// The size of the internal container inner_container_size: Size, } impl Forest { + /// Computes the layout of this [`Forest`] according to the flexbox algorithm 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() @@ -78,9 +120,9 @@ impl Forest { || style.max_size.height.is_defined(); let result = if has_root_min_max { - let first_pass = self.compute_internal(root, style.size.resolve(size), size, false, true); + let first_pass = self.compute_preliminary(root, style.size.resolve(size), size, false, true); - self.compute_internal( + self.compute_preliminary( root, Size { width: first_pass @@ -101,7 +143,7 @@ impl Forest { true, ) } else { - self.compute_internal(root, style.size.resolve(size), size, true, true) + self.compute_preliminary(root, style.size.resolve(size), size, true, true) }; self.nodes[root].layout = Layout { order: 0, size: result.size, location: Point::zero() }; @@ -109,6 +151,7 @@ impl Forest { Self::round_layout(&mut self.nodes, &self.children, root, 0.0, 0.0); } + /// Rounds the calculated [`NodeData`] according to the spec fn round_layout(nodes: &mut [NodeData], children: &[ChildrenVec], root: NodeId, abs_x: f32, abs_y: f32) { let layout = &mut nodes[root].layout; let abs_x = abs_x + layout.location.x; @@ -125,6 +168,7 @@ impl Forest { } } + /// Saves intermediate results to a [`Cache`] fn cache(&mut self, node: NodeId, main_size: bool) -> &mut Option { if main_size { &mut self.nodes[node].main_size_layout_cache @@ -380,7 +424,7 @@ impl Forest { }; child.flex_basis = self - .compute_internal( + .compute_preliminary( child.node, Size { width: width.maybe_max(child.min_size.width).maybe_min(child.max_size.width), @@ -409,7 +453,7 @@ impl Forest { // webkit handled various scenarios. Can probably be solved better by passing in // min-content max-content constraints from the top let min_main = self - .compute_internal(child.node, Size::undefined(), available_space, false, false) + .compute_preliminary(child.node, Size::undefined(), available_space, false, false) .size .main(constants.dir) .maybe_max(child.min_size.main(constants.dir)) @@ -518,7 +562,7 @@ impl Forest { if constants.node_inner_size.main(constants.dir).is_undefined() && constants.is_row { child.target_size.set_main( constants.dir, - self.compute_internal( + self.compute_preliminary( child.node, Size { width: child.size.width.maybe_max(child.min_size.width).maybe_min(child.max_size.width), @@ -670,7 +714,7 @@ impl Forest { // min-content max-content constraints from the top. Need to figure out correct thing to do here as // just piling on more conditionals. let min_main = if constants.is_row && self.nodes[child.node].measure.is_none() { - self.compute_internal(child.node, Size::undefined(), available_space, false, false) + self.compute_preliminary(child.node, Size::undefined(), available_space, false, false) .size .width .maybe_min(child.size.width) @@ -735,7 +779,7 @@ impl Forest { child.hypothetical_inner_size.set_cross( constants.dir, - self.compute_internal( + self.compute_preliminary( child.node, Size { width: if constants.is_row { child.target_size.width.into() } else { child_cross }, @@ -778,6 +822,7 @@ impl Forest { flex_lines: &mut [FlexLine], constants: &AlgoConstants, ) { + /// Recursively caluculates the baseline for children fn calc_baseline(db: &Forest, node: NodeId, layout: &Layout) -> f32 { if db.children[node].is_empty() { layout.size.height @@ -789,7 +834,7 @@ impl Forest { for line in flex_lines { for child in line.items.iter_mut() { - let result = self.compute_internal( + let result = self.compute_preliminary( child.node, Size { width: if constants.is_row { @@ -1301,7 +1346,7 @@ impl Forest { let line_offset_cross = line.offset_cross; let layout_item = |child: &mut FlexItem| { - let result = self.compute_internal( + let result = self.compute_preliminary( child.node, child.target_size.map(|s| s.into()), constants.container_size.map(|s| s.into()), @@ -1404,7 +1449,7 @@ impl Forest { Undefined }); - let result = self.compute_internal( + let result = self.compute_preliminary( child, Size { width, height }, Size { width: container_width, height: container_height }, @@ -1498,7 +1543,8 @@ impl Forest { } } - fn compute_internal( + /// Compute a preliminary size for an item + fn compute_preliminary( &mut self, node: NodeId, node_size: Size, @@ -1662,6 +1708,9 @@ impl Forest { // Before returning we perform absolute layout on all absolutely positioned children self.perform_absolute_layout_on_absolute_children(node, &constants); + /// Lay out all hidden nodes recursively + /// + /// Each hidden node has zero size and is placed at the origin fn hidden_layout(nodes: &mut [NodeData], children: &[ChildrenVec], node: NodeId, order: u32) { nodes[node].layout = Layout { order, size: Size::zero(), location: Point::zero() }; diff --git a/src/geometry.rs b/src/geometry.rs index 9fc7ab6f2..55555948c 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -234,6 +234,7 @@ impl Size { } 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) } } diff --git a/src/node.rs b/src/node.rs index 043769bea..477ea4878 100644 --- a/src/node.rs +++ b/src/node.rs @@ -224,7 +224,7 @@ impl Sprawl { let id = self.find_node(parent)?; Ok(self.forest.children[id].len()) } - + /// Returns a list of children that belong to the [`Parent`] pub fn children(&self, parent: Node) -> Result, Error> { let id = self.find_node(parent)?; @@ -282,18 +282,19 @@ pub(crate) struct Id(usize); /// An bump-allocator index that tracks how many [`Nodes`](Node) have been allocated in a [`Sprawl`]. pub(crate) struct Allocator { - new_id: AtomicUsize, + /// The last reserved [`NodeId`] + last_id: AtomicUsize, } impl Allocator { /// Creates a fresh [`Allocator`] #[must_use] pub const fn new() -> Self { - Self { new_id: AtomicUsize::new(0) } + Self { last_id: AtomicUsize::new(0) } } /// Allocates space for one more [`Node`] pub fn allocate(&self) -> Id { - Id(self.new_id.fetch_add(1, Ordering::Relaxed)) + Id(self.last_id.fetch_add(1, Ordering::Relaxed)) } } diff --git a/src/style.rs b/src/style.rs index 762c6b643..ff95ab96d 100644 --- a/src/style.rs +++ b/src/style.rs @@ -6,7 +6,7 @@ use crate::number::Number; /// How [`Nodes`](crate::node::Node) are aligned relative to the cross axis /// /// The default behavior is [`AlignItems::Stretch`]. -/// +/// /// [Specification](https://www.w3.org/TR/css-flexbox-1/#align-items-property) #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -34,7 +34,7 @@ impl Default for AlignItems { /// The behavior of any child nodes will be controlled by this node's [`AlignItems`] value. /// /// The default behavior is [`AlignSelf::Auto`]. -/// +/// /// [Specification](https://www.w3.org/TR/css-flexbox-1/#align-items-property) #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -62,7 +62,7 @@ impl Default for AlignSelf { /// Sets the distribution of space between and around content items along the cross-axis /// /// The default value is [`AlignContent::Stretch`]. -/// +/// /// [Specification](https://www.w3.org/TR/css-flexbox-1/#align-content-property) #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -117,7 +117,7 @@ impl Default for Display { /// Items are always aligned relative to the cross axis, and justified relative to the main axis. /// /// The default behavior is [`FlexDirection::Row`]. -/// +/// /// [Specification](https://www.w3.org/TR/css-flexbox-1/#flex-direction-property) #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -148,16 +148,19 @@ impl Default for FlexDirection { impl FlexDirection { #[inline] + /// Is the direction [`FlexDirection::Row`] or [`FlexDirection::RowReverse`]? pub(crate) fn is_row(self) -> bool { matches!(self, Self::Row | Self::RowReverse) } #[inline] + /// Is the direction [`FlexDirection::Column`] or [`FlexDirection::ColumnReverse`]? pub(crate) fn is_column(self) -> bool { matches!(self, Self::Column | Self::ColumnReverse) } #[inline] + /// Is the direction [`FlexDirection::RowReverse`] or [`FlexDirection::ColumnReverse`]? pub(crate) fn is_reverse(self) -> bool { matches!(self, Self::RowReverse | Self::ColumnReverse) } @@ -166,7 +169,7 @@ impl FlexDirection { /// Sets the distribution of space between and around content items along the main-axis /// /// The default value is [`JustifyContent::FlexStart`]. -/// +/// /// [Specification](https://www.w3.org/TR/css-flexbox-1/#justify-content-property) #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -223,9 +226,9 @@ impl Default for PositionType { } /// Controls whether flex items are forced onto one line or can wrap onto multiple lines. -/// +/// /// Defaults to [`FlexWrap::NoWrap`] -/// +/// /// [Specification](https://www.w3.org/TR/css-flexbox-1/#flex-wrap-property) #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -389,6 +392,7 @@ impl Default for Style { } impl Style { + /// If the `direction` is row-oriented, the min width. Otherwise the min height pub(crate) fn min_main_size(&self, direction: FlexDirection) -> Dimension { if direction.is_row() { self.min_size.width @@ -397,6 +401,7 @@ impl Style { } } + /// If the `direction` is row-oriented, the max width. Otherwise the max height pub(crate) fn max_main_size(&self, direction: FlexDirection) -> Dimension { if direction.is_row() { self.max_size.width @@ -405,6 +410,7 @@ impl Style { } } + /// If the `direction` is row-oriented, the margin start. Otherwise the margin top pub(crate) fn main_margin_start(&self, direction: FlexDirection) -> Dimension { if direction.is_row() { self.margin.start @@ -413,6 +419,7 @@ impl Style { } } + /// If the `direction` is row-oriented, the margin end. Otherwise the margin bottom pub(crate) fn main_margin_end(&self, direction: FlexDirection) -> Dimension { if direction.is_row() { self.margin.end @@ -421,6 +428,7 @@ impl Style { } } + /// If the `direction` is row-oriented, the height. Otherwise the width pub(crate) fn cross_size(&self, direction: FlexDirection) -> Dimension { if direction.is_row() { self.size.height @@ -429,6 +437,7 @@ impl Style { } } + /// If the `direction` is row-oriented, the min height. Otherwise the min width pub(crate) fn min_cross_size(&self, direction: FlexDirection) -> Dimension { if direction.is_row() { self.min_size.height @@ -437,6 +446,7 @@ impl Style { } } + /// If the `direction` is row-oriented, the max height. Otherwise the max width pub(crate) fn max_cross_size(&self, direction: FlexDirection) -> Dimension { if direction.is_row() { self.max_size.height @@ -445,6 +455,7 @@ impl Style { } } + /// If the `direction` is row-oriented, the margin top. Otherwise the margin start pub(crate) fn cross_margin_start(&self, direction: FlexDirection) -> Dimension { if direction.is_row() { self.margin.top @@ -453,6 +464,7 @@ impl Style { } } + /// If the `direction` is row-oriented, the margin bottom. Otherwise the margin end pub(crate) fn cross_margin_end(&self, direction: FlexDirection) -> Dimension { if direction.is_row() { self.margin.bottom @@ -461,6 +473,7 @@ impl Style { } } + /// Computes the final alignment of this item based on the parent's [`AlignItems`] and this item's [`AlignSelf`] pub(crate) fn align_self(&self, parent: &Style) -> AlignSelf { if self.align_self == AlignSelf::Auto { match parent.align_items { diff --git a/src/sys.rs b/src/sys.rs index 086f159d7..1c54af08c 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -12,6 +12,7 @@ pub use self::alloc::*; #[cfg(all(not(feature = "alloc"), not(feature = "std")))] pub use self::core::*; +/// For when `std` is enabled #[cfg(feature = "std")] mod std { /// An allocation-backend agnostic [`Box`] type @@ -53,6 +54,7 @@ mod std { } } +/// For when `alloc` but not `std` is enabled #[cfg(all(feature = "alloc", not(feature = "std")))] mod alloc { extern crate alloc; @@ -93,6 +95,7 @@ mod alloc { } } +/// For when neither `alloc` nor `std` is enabled #[cfg(all(not(feature = "alloc"), not(feature = "std")))] mod core { /// The maximum number of nodes in the forest From cb9ee010b10b179230f472fe6890c8e4d0164a6d Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Fri, 10 Jun 2022 12:51:46 -0400 Subject: [PATCH 35/35] Cargo fmt --- src/forest.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/forest.rs b/src/forest.rs index 686bb5f0c..6d983cf08 100644 --- a/src/forest.rs +++ b/src/forest.rs @@ -81,7 +81,7 @@ pub(crate) struct Forest { impl Forest { /// Creates a new [`Forest`] that can store up to `capacity` [`Nodes`](crate::node::Node) before needing to be expanded - #[must_use] + #[must_use] pub(crate) fn with_capacity(capacity: usize) -> Self { Self { nodes: new_vec_with_capacity(capacity), diff --git a/src/lib.rs b/src/lib.rs index c16842925..ed5ec0143 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,9 +20,9 @@ pub mod style; mod flexbox; mod forest; -mod sys; #[cfg(all(not(feature = "alloc"), not(feature = "std")))] mod indexmap; +mod sys; pub use crate::node::Sprawl;