tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 583HTML 275Markdown 8TOML 41586 Definiciones

⎇ float-layout src/compute/float.rs Rust · 146 líneas · 6.5 KB Historia Quién tocó qué crudo

Setup BFC inheritance 3a7cffaca · Nico Burns ·

1//! Computes the position of floats in a block formatting context
2//!
3//! Here are the precise rules that govern the behavior of floats:
4//!
5//! 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
6//!    holds for right-floating elements.
7//! 2. If the current box is left-floating, and there are any left-floating boxes generated by elements earlier in the source document,
8//!    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,
9//!    or its top must be lower than the bottom of the earlier box. Analogous rules hold for right-floating boxes.
10//! 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.
11//!    Analogous rules hold for right-floating elements.
12//! 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,
13//!    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
14//!    by the rules in the section on margin collapsing.
15//! 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.
16//! 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
17//!    an element earlier in the source document.
18//! 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
19//!    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
20//!    holds for right-floating elements.
21//! 8. A floating box must be placed as high as possible.
22//! 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
23//!    preferred over one that is further to the left/right.
24//!
25//! 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.
26//!
27//! <https://www.w3.org/TR/CSS22/visuren.html#floats>
28
29use crate::{AvailableSpace, Point};
30
31/// A context for placing floated boxes
32#[derive(Debug, Clone)]
33pub struct FloatContext {
34    available_space: AvailableSpace,
35    /// A list of left-floated boxes within the context
36    left_floats: Vec<PlacedFloatedBox>,
37    /// A list of right-floated boxes within the context
38    right_floats: Vec<PlacedFloatedBox>,
39    /// A list of non-overlapping horizontal segments within the context.
40    /// Each segment has the same available width for it's entire height.
41    segments: Vec<Segment>,
42}
43
44#[derive(Debug, Copy, Clone, PartialEq, Eq)]
45pub enum FloatDirection {
46    Left,
47    Right,
48}
49
50/// A floated box to place within the context
51#[derive(Debug, Clone, Default)]
52pub struct FloatedBox {
53    /// A user defined ID for the box
54    id: u64,
55    width: f32,
56    height: f32,
57}
58
59/// A floated box
60#[derive(Debug, Clone, Default)]
61struct PlacedFloatedBox {
62    /// A user defined ID for the box
63    id: u64,
64    width: f32,
65    height: f32,
66    /// Distance from the edge of the container that the box is floated towards
67    /// (distance from the left for left floats, from the right for right floats)
68    x_inset: f32,
69    y: f32,
70}
71
72impl PlacedFloatedBox {
73    const DEFAULT: Self = Self { id: 0, width: 0.0, height: 0.0, x_inset: 0.0, y: 0.0 };
74}
75
76#[derive(Debug, Clone)]
77struct Segment {
78    y_start: f32,
79    y_end: f32,
80    inset_left: f32,
81    inset_right: f32,
82}
83
84impl FloatContext {
85    /// Create a new empty `FloatContext`
86    pub fn new(available_space: AvailableSpace) -> Self {
87        Self { available_space, left_floats: Vec::new(), right_floats: Vec::new(), segments: Vec::new() }
88    }
89
90    /// Position a floated box with the context
91    pub fn place_floated_box(&mut self, floated_box: FloatedBox, float_direction: FloatDirection) -> Point<f32> {
92        let last_float = self.get_float_list(float_direction).last().unwrap_or(&PlacedFloatedBox::DEFAULT);
93
94        let (x_inset, y): (f32, f32) = match self.available_space {
95            AvailableSpace::MinContent => {
96                let x_inset = 0.0;
97                let y = last_float.y + last_float.height;
98                (x_inset, y)
99            }
100            AvailableSpace::MaxContent => {
101                let x_inset = last_float.x_inset + last_float.width;
102                let y = last_float.y;
103                (x_inset, y)
104            }
105            AvailableSpace::Definite(available_width) => {
106                let line_available_width = available_width - last_float.x_inset - last_float.width;
107
108                if line_available_width < floated_box.width {
109                    let x_inset = 0.0;
110                    let y = last_float.y + last_float.height;
111                    (x_inset, y)
112                } else {
113                    let x_inset = last_float.x_inset + last_float.width;
114                    let y = last_float.y;
115                    (x_inset, y)
116                }
117            }
118        };
119
120        let placed_floated_box =
121            PlacedFloatedBox { id: floated_box.id, width: floated_box.width, height: floated_box.height, x_inset, y };
122
123        match float_direction {
124            FloatDirection::Left => self.left_floats.push(placed_floated_box),
125            FloatDirection::Right => self.right_floats.push(placed_floated_box),
126        }
127
128        // Return the (x, y) coordinates of the positioned box
129        return match self.available_space {
130            // Position won't actually be used if we're layouting under a min-content
131            // or max-content constraint, so just return (0, 0)
132            AvailableSpace::MinContent | AvailableSpace::MaxContent => Point::ZERO,
133            AvailableSpace::Definite(width) => match float_direction {
134                FloatDirection::Left => Point { x: x_inset, y },
135                FloatDirection::Right => Point { x: width - x_inset, y },
136            },
137        };
138    }
139
140    fn get_float_list(&self, float_direction: FloatDirection) -> &[PlacedFloatedBox] {
141        match float_direction {
142            FloatDirection::Left => &self.left_floats,
143            FloatDirection::Right => &self.right_floats,
144        }
145    }
146}