tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5544Rust 88Markdown 11TOML 91926 Sutichaykuna

Float layout WIP

22a79ba0ee895068d1c4b0598cbec5f479561b17 · ruwaq Nico Burns · (2 wata ñawpaq) · tayta 79e45897f · kay commitpi sach'ata qhaway

Semántico sayk'uy · 18

src/compute/float.rs

src/compute/mod.rs

src/style/block.rs

src/style/float.rs

src/style/mod.rs

6 qillqakuna · +161 −0

diff --git a/Cargo.toml b/Cargo.toml
index ce446ef03..ee50819cb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,6 +38,7 @@ default = [
     "flexbox",
     "grid",
     "block_layout",
+    "float_layout",
     "calc",
     "content_size",
     "detailed_layout_info",
@@ -48,6 +49,8 @@ default = [
 
 ## Enables the Block layout algorithm. See [`compute_block_layout`](crate::compute_block_layout).
 block_layout = []
+## Enables the Float layout algorithm. This is a sub-feature of block layout.
+float_layout = []
 ## Enables the Flexbox layout algorithm. See [`compute_flexbox_layout`](crate::compute_flexbox_layout).
 flexbox = []
 ## Enables the CSS Grid layout algorithm. See [`compute_grid_layout`](crate::compute_grid_layout).
diff --git a/src/compute/float.rs b/src/compute/float.rs
new file mode 100644
index 000000000..9fac7d16c
--- /dev/null
+++ b/src/compute/float.rs
@@ -0,0 +1,101 @@
+//! Computes the position of floats in a block formatting context
+//!
+//! Here are the precise rules that govern the behavior of floats:
+//!
+//! 1. The left outer edge of a left-floating box may not be to the left of the left edge of its containing block. An analogous rule
+//!    holds for right-floating elements.
+//! 2. If the current box is left-floating, and there are any left-floating boxes generated by elements earlier in the source document,
+//!    then for each such earlier box, either the left outer edge of the current box must be to the right of the right outer edge of the earlier box,
+//!    or its top must be lower than the bottom of the earlier box. Analogous rules hold for right-floating boxes.
+//! 3. The right outer edge of a left-floating box may not be to the right of the left outer edge of any right-floating box that is next to it.
+//!    Analogous rules hold for right-floating elements.
+//! 4. A floating box's outer top may not be higher than the top of its containing block. When the float occurs between two collapsing margins,
+//!    the float is positioned as if it had an otherwise empty anonymous block parent taking part in the flow. The position of such a parent is defined
+//!    by the rules in the section on margin collapsing.
+//! 5. The outer top of a floating box may not be higher than the outer top of any block or floated box generated by an element earlier in the source document.
+//! 6. The outer top of an element's floating box may not be higher than the top of any line-box containing a box generated by
+//!    an element earlier in the source document.
+//! 7. A left-floating box that has another left-floating box to its left may not have its right outer edge to the right of its containing block's
+//!    right edge. (Loosely: a left float may not stick out at the right edge, unless it is already as far to the left as possible.) An analogous rule
+//!    holds for right-floating elements.
+//! 8. A floating box must be placed as high as possible.
+//! 9. A left-floating box must be put as far to the left as possible, a right-floating box as far to the right as possible. A higher position is
+//!    preferred over one that is further to the left/right.
+//!
+//! But in CSS 2.2, if, within the block formatting context, there is an in-flow negative vertical margin such that the float's position is above the position it would be at were all such negative margins set to zero, the position of the float is undefined.
+//!
+//! <https://www.w3.org/TR/CSS22/visuren.html#floats>
+
+use crate::AvailableSpace;
+
+/// A context for placing floated boxes
+pub struct FloatContext {
+    available_width: f32,
+    /// A list of left-floated boxes within the context
+    left_floats: Vec<PlacedFloatedBox>,
+    /// A list of right-floated boxes within the context
+    right_floats: Vec<PlacedFloatedBox>,
+    /// A list of non-overlapping horizontal segments within the context.
+    /// Each segment has the same available width for it's entire height.
+    segments: Vec<Segment>,
+}
+
+pub enum FloatDirection {
+    Left,
+    Right,
+}
+
+/// A floated box to place within the context
+#[derive(Default)]
+pub struct FloatedBox {
+    /// A user defined ID for the box
+    id: u64,
+    width: f32,
+    height: f32,
+}
+
+/// A floated box
+#[derive(Default)]
+struct PlacedFloatedBox {
+    /// Distance from the edge of the container that the box is floated towards
+    /// (distance from the left for left floats, from the right for right floats)
+    x_inset: f32,
+    y: f32,
+    inner: FloatedBox,
+}
+
+impl PlacedFloatedBox {
+    const DEFAULT: Self = Self { x_inset: 0.0, y: 0.0, inner: FloatedBox { id: 0, width: 0.0, height: 0.0 } };
+}
+
+struct Segment {
+    y_start: f32,
+    y_end: f32,
+    inset_left: f32,
+    inset_right: f32,
+}
+
+impl FloatContext {
+    /// Create a new empty `FloatContext`
+    pub fn new(available_width: f32) -> Self {
+        Self { available_width, left_floats: Vec::new(), right_floats: Vec::new(), segments: Vec::new() }
+    }
+
+    /// Position a floated box with the context
+    pub fn place_box(&mut self, floated_box: FloatedBox, float_direction: FloatDirection) {
+        let last_float = self.get_float_list(float_direction).last().unwrap_or(&PlacedFloatedBox::DEFAULT);
+
+        let y_threshold = if self.available_width - last_float.inner.width < 0.0 {
+            last_float.y + last_float.inner.height
+        } else {
+            last_float.y
+        };
+    }
+
+    fn get_float_list(&self, float_direction: FloatDirection) -> &[PlacedFloatedBox] {
+        match float_direction {
+            FloatDirection::Left => &self.left_floats,
+            FloatDirection::Right => &self.right_floats,
+        }
+    }
+}
diff --git a/src/compute/mod.rs b/src/compute/mod.rs
index aeb5a5f3e..45ea25698 100644
--- a/src/compute/mod.rs
+++ b/src/compute/mod.rs
@@ -27,6 +27,9 @@ pub(crate) mod leaf;
 #[cfg(feature = "block_layout")]
 pub(crate) mod block;
 
+#[cfg(feature = "float_layout")]
+pub(crate) mod float;
+
 #[cfg(feature = "flexbox")]
 pub(crate) mod flexbox;
 
@@ -44,6 +47,9 @@ pub use self::flexbox::compute_flexbox_layout;
 #[cfg(feature = "grid")]
 pub use self::grid::compute_grid_layout;
 
+#[cfg(feature = "float_layout")]
+pub use self::float::{FloatContext, FloatedBox};
+
 use crate::geometry::{Line, Point, Size};
 use crate::style::{AvailableSpace, CoreStyle, Overflow};
 use crate::tree::{
diff --git a/src/style/block.rs b/src/style/block.rs
index 187698e54..30ee7ebdf 100644
--- a/src/style/block.rs
+++ b/src/style/block.rs
@@ -17,6 +17,13 @@ pub trait BlockItemStyle: CoreStyle {
     fn is_table(&self) -> bool {
         false
     }
+
+    /// Whether the item is a floated
+    #[cfg(feature = "float_layout")]
+    #[inline(always)]
+    fn float(&self) -> super::Float {
+        super::Float::None
+    }
 }
 
 /// Used by block layout to implement the legacy behaviour of `<center>` and `<div align="left | right | center">`
diff --git a/src/style/float.rs b/src/style/float.rs
new file mode 100644
index 000000000..1ecfc1ca2
--- /dev/null
+++ b/src/style/float.rs
@@ -0,0 +1,33 @@
+//! Style types for float layout
+
+/// Floats a box to the left or right.
+/// This property only applies to children of a block layout
+///
+/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/float>
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub enum Float {
+    /// The box is floated to the left
+    Left,
+    /// The box is floated to the right
+    Right,
+    /// The box is not floated
+    #[default]
+    None,
+}
+
+/// Gives a box "clearance", which moves it below floated boxes which precede
+/// it in the tree.
+///
+/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/clear>
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub enum Clear {
+    /// The box clears left-floated boxes
+    Left,
+    /// The box clears right-floated boxes
+    Right,
+    /// The box clears boxes floated in either direction
+    Both,
+    /// The box does not clear floated boxes
+    #[default]
+    None,
+}
diff --git a/src/style/mod.rs b/src/style/mod.rs
index e44d06b74..983938703 100644
--- a/src/style/mod.rs
+++ b/src/style/mod.rs
@@ -8,6 +8,8 @@ mod dimension;
 mod block;
 #[cfg(feature = "flexbox")]
 mod flex;
+#[cfg(feature = "float_layout")]
+mod float;
 #[cfg(feature = "grid")]
 mod grid;
 
@@ -21,6 +23,8 @@ use crate::sys::DefaultCheapStr;
 pub use self::block::{BlockContainerStyle, BlockItemStyle, TextAlign};
 #[cfg(feature = "flexbox")]
 pub use self::flex::{FlexDirection, FlexWrap, FlexboxContainerStyle, FlexboxItemStyle};
+#[cfg(feature = "float_layout")]
+pub use self::float::{Clear, Float};
 #[cfg(feature = "grid")]
 pub use self::grid::{
     GenericGridPlacement, GenericGridTemplateComponent, GenericRepetition, GridAutoFlow, GridContainerStyle,
@@ -391,6 +395,10 @@ pub struct Style<S: CheapCloneStr = DefaultCheapStr> {
     /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
     pub scrollbar_width: f32,
 
+    #[cfg(feature = "float_layout")]
+    /// Should the box be floated
+    pub float: Float,
+
     // Position properties
     /// What should the `position` value of this struct use as a base offset?
     pub position: Position,
@@ -525,6 +533,8 @@ impl<S: CheapCloneStr> Style<S> {
         box_sizing: BoxSizing::BorderBox,
         overflow: Point { x: Overflow::Visible, y: Overflow::Visible },
         scrollbar_width: 0.0,
+        #[cfg(feature = "float_layout")]
+        float: Float::None,
         position: Position::Relative,
         inset: Rect::auto(),
         margin: Rect::zero(),
@@ -1093,6 +1103,7 @@ mod tests {
             item_is_table: false,
             item_is_replaced: false,
             box_sizing: Default::default(),
+            float: Default::default(),
             overflow: Default::default(),
             scrollbar_width: 0.0,
             position: Default::default(),