1use crate::fragment::Fragment;
8use crate::geometry::{Point, Rect};
9use crate::style::{Clear, Float};
10
11#[derive(Debug, Clone)]
16pub struct FloatContext {
17 containing_width: f32,
19 left_floats: Vec<FloatBox>,
21 right_floats: Vec<FloatBox>,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq)]
27struct FloatBox {
28 rect: Rect,
30}
31
32impl FloatContext {
33 pub fn new(containing_width: f32) -> Self {
38 Self {
39 containing_width,
40 left_floats: Vec::new(),
41 right_floats: Vec::new(),
42 }
43 }
44
45 pub fn place_float(
61 &mut self,
62 mut fragment: Fragment,
63 float_type: Float,
64 cursor_y: f32,
65 ) -> Fragment {
66 let margin_box = fragment.margin_box();
67 let width = margin_box.width;
68 let height = margin_box.height;
69
70 let min_y = self.compute_min_y_for_float(&float_type, cursor_y);
72 let mut y = min_y;
73
74 loop {
76 let (left_offset, available_width) = self.available_width_at(y, height);
77
78 if available_width >= width {
79 let x = match float_type {
81 Float::Left => left_offset,
82 Float::Right => left_offset + available_width - width,
83 Float::None => {
84 left_offset
86 }
87 };
88
89 let border_x = x + fragment.margin.left;
92 let border_y = y + fragment.margin.top;
93
94 fragment.position = Point::new(border_x, border_y);
95
96 let float_box = FloatBox {
98 rect: Rect::new(x, y, width, height),
99 };
100
101 match float_type {
102 Float::Left => self.left_floats.push(float_box),
103 Float::Right => self.right_floats.push(float_box),
104 Float::None => {}
105 }
106
107 return fragment;
108 }
109
110 y = self.next_y_position(y, height);
112
113 if y > cursor_y + 100000.0 {
115 let x = match float_type {
117 Float::Left => 0.0,
118 Float::Right => self.containing_width - width,
119 Float::None => 0.0,
120 }
121 .max(0.0);
122
123 let border_x = x + fragment.margin.left;
124 let border_y = y + fragment.margin.top;
125 fragment.position = Point::new(border_x, border_y);
126
127 let float_box = FloatBox {
128 rect: Rect::new(x, y, width, height),
129 };
130
131 match float_type {
132 Float::Left => self.left_floats.push(float_box),
133 Float::Right => self.right_floats.push(float_box),
134 Float::None => {}
135 }
136
137 return fragment;
138 }
139 }
140 }
141
142 pub fn available_width_at(&self, y: f32, height: f32) -> (f32, f32) {
153 let bottom = y + height;
154
155 let left_edge = self
157 .left_floats
158 .iter()
159 .filter(|f| f.rect.y < bottom && f.rect.bottom() > y)
160 .map(|f| f.rect.right())
161 .fold(0.0_f32, f32::max);
162
163 let right_edge = self
165 .right_floats
166 .iter()
167 .filter(|f| f.rect.y < bottom && f.rect.bottom() > y)
168 .map(|f| f.rect.x)
169 .fold(self.containing_width, f32::min);
170
171 let available = (right_edge - left_edge).max(0.0);
172 (left_edge, available)
173 }
174
175 pub fn clear(&self, clear: Clear) -> f32 {
183 match clear {
184 Clear::None => 0.0,
185 Clear::Left => self.bottom_of_floats(&self.left_floats),
186 Clear::Right => self.bottom_of_floats(&self.right_floats),
187 Clear::Both => {
188 let left_bottom = self.bottom_of_floats(&self.left_floats);
189 let right_bottom = self.bottom_of_floats(&self.right_floats);
190 left_bottom.max(right_bottom)
191 }
192 }
193 }
194
195 pub fn clear_all(&self) -> f32 {
200 let left_bottom = self.bottom_of_floats(&self.left_floats);
201 let right_bottom = self.bottom_of_floats(&self.right_floats);
202 left_bottom.max(right_bottom)
203 }
204
205 fn compute_min_y_for_float(&self, float_type: &Float, cursor_y: f32) -> f32 {
207 let same_side_bottom = match float_type {
208 Float::Left => self.bottom_of_floats(&self.left_floats),
209 Float::Right => self.bottom_of_floats(&self.right_floats),
210 Float::None => 0.0,
211 };
212 cursor_y.max(same_side_bottom)
213 }
214
215 fn next_y_position(&self, current_y: f32, height: f32) -> f32 {
217 let bottom = current_y + height;
218
219 let mut next_positions = Vec::new();
221
222 for float_box in self.left_floats.iter().chain(self.right_floats.iter()) {
223 if float_box.rect.y < bottom && float_box.rect.bottom() > current_y {
225 next_positions.push(float_box.rect.bottom());
226 }
227 if float_box.rect.y >= current_y {
229 next_positions.push(float_box.rect.y);
230 }
231 }
232
233 next_positions
235 .into_iter()
236 .filter(|&y| y > current_y)
237 .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
238 .unwrap_or(current_y + 1.0)
239 }
240
241 fn bottom_of_floats(&self, floats: &[FloatBox]) -> f32 {
243 floats
244 .iter()
245 .map(|f| f.rect.bottom())
246 .fold(0.0_f32, f32::max)
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253 use crate::fragment::FragmentKind;
254 use crate::geometry::{Edges, Size};
255 use crate::tree::NodeId;
256
257 fn create_fragment(width: f32, height: f32, margin: f32) -> Fragment {
258 let mut frag = Fragment::new(NodeId(0), FragmentKind::Box);
259 frag.size = Size::new(width, height);
260 frag.margin = Edges::all(margin);
261 frag
262 }
263
264 #[test]
265 fn test_new_float_context() {
266 let ctx = FloatContext::new(800.0);
267 assert_eq!(ctx.containing_width, 800.0);
268 assert_eq!(ctx.left_floats.len(), 0);
269 assert_eq!(ctx.right_floats.len(), 0);
270 }
271
272 #[test]
273 fn test_available_width_no_floats() {
274 let ctx = FloatContext::new(800.0);
275 let (left, width) = ctx.available_width_at(0.0, 100.0);
276 assert_eq!(left, 0.0);
277 assert_eq!(width, 800.0);
278 }
279
280 #[test]
281 fn test_place_left_float() {
282 let mut ctx = FloatContext::new(800.0);
283 let frag = create_fragment(100.0, 50.0, 10.0);
284
285 let placed = ctx.place_float(frag, Float::Left, 0.0);
286
287 assert_eq!(placed.position.x, 10.0);
289 assert_eq!(placed.position.y, 10.0);
290
291 assert_eq!(ctx.left_floats.len(), 1);
293
294 let margin_box = placed.margin_box();
296 assert_eq!(margin_box.x, 0.0);
297 assert_eq!(margin_box.y, 0.0);
298 assert_eq!(margin_box.width, 120.0); assert_eq!(margin_box.height, 70.0); }
301
302 #[test]
303 fn test_place_right_float() {
304 let mut ctx = FloatContext::new(800.0);
305 let frag = create_fragment(100.0, 50.0, 10.0);
306
307 let placed = ctx.place_float(frag, Float::Right, 0.0);
308
309 assert_eq!(placed.position.x, 690.0);
312 assert_eq!(placed.position.y, 10.0);
313
314 assert_eq!(ctx.right_floats.len(), 1);
315 }
316
317 #[test]
318 fn test_two_left_floats_side_by_side() {
319 let mut ctx = FloatContext::new(800.0);
320
321 let frag1 = create_fragment(100.0, 50.0, 0.0);
322 let placed1 = ctx.place_float(frag1, Float::Left, 0.0);
323 assert_eq!(placed1.position.x, 0.0);
324 assert_eq!(placed1.position.y, 0.0);
325
326 let frag2 = create_fragment(100.0, 50.0, 0.0);
327 let placed2 = ctx.place_float(frag2, Float::Left, 0.0);
328
329 assert_eq!(placed2.position.x, 0.0);
331 assert_eq!(placed2.position.y, 50.0);
332 }
333
334 #[test]
335 fn test_left_float_wraps_when_no_space() {
336 let mut ctx = FloatContext::new(200.0);
337
338 let frag1 = create_fragment(150.0, 50.0, 0.0);
340 ctx.place_float(frag1, Float::Left, 0.0);
341
342 let frag2 = create_fragment(100.0, 40.0, 0.0);
344 let placed2 = ctx.place_float(frag2, Float::Left, 0.0);
345
346 assert_eq!(placed2.position.x, 0.0);
348 assert_eq!(placed2.position.y, 50.0); }
350
351 #[test]
352 fn test_available_width_with_left_float() {
353 let mut ctx = FloatContext::new(800.0);
354 let frag = create_fragment(200.0, 100.0, 0.0);
355 ctx.place_float(frag, Float::Left, 0.0);
356
357 let (left, width) = ctx.available_width_at(50.0, 10.0);
358 assert_eq!(left, 200.0); assert_eq!(width, 600.0); }
361
362 #[test]
363 fn test_available_width_with_right_float() {
364 let mut ctx = FloatContext::new(800.0);
365 let frag = create_fragment(200.0, 100.0, 0.0);
366 ctx.place_float(frag, Float::Right, 0.0);
367
368 let (left, width) = ctx.available_width_at(50.0, 10.0);
369 assert_eq!(left, 0.0);
370 assert_eq!(width, 600.0); }
372
373 #[test]
374 fn test_available_width_with_both_floats() {
375 let mut ctx = FloatContext::new(800.0);
376
377 let left_frag = create_fragment(150.0, 100.0, 0.0);
378 ctx.place_float(left_frag, Float::Left, 0.0);
379
380 let right_frag = create_fragment(200.0, 100.0, 0.0);
381 ctx.place_float(right_frag, Float::Right, 0.0);
382
383 let (left, width) = ctx.available_width_at(50.0, 10.0);
384 assert_eq!(left, 150.0); assert_eq!(width, 450.0); }
387
388 #[test]
389 fn test_available_width_below_floats() {
390 let mut ctx = FloatContext::new(800.0);
391 let frag = create_fragment(200.0, 100.0, 0.0);
392 ctx.place_float(frag, Float::Left, 0.0);
393
394 let (left, width) = ctx.available_width_at(150.0, 10.0);
396 assert_eq!(left, 0.0); assert_eq!(width, 800.0);
398 }
399
400 #[test]
401 fn test_clear_none() {
402 let mut ctx = FloatContext::new(800.0);
403 let frag = create_fragment(100.0, 50.0, 0.0);
404 ctx.place_float(frag, Float::Left, 0.0);
405
406 assert_eq!(ctx.clear(Clear::None), 0.0);
407 }
408
409 #[test]
410 fn test_clear_left() {
411 let mut ctx = FloatContext::new(800.0);
412 let frag = create_fragment(100.0, 50.0, 0.0);
413 ctx.place_float(frag, Float::Left, 0.0);
414
415 assert_eq!(ctx.clear(Clear::Left), 50.0);
416 }
417
418 #[test]
419 fn test_clear_right() {
420 let mut ctx = FloatContext::new(800.0);
421 let frag = create_fragment(100.0, 60.0, 0.0);
422 ctx.place_float(frag, Float::Right, 0.0);
423
424 assert_eq!(ctx.clear(Clear::Right), 60.0);
425 }
426
427 #[test]
428 fn test_clear_both() {
429 let mut ctx = FloatContext::new(800.0);
430
431 let left_frag = create_fragment(100.0, 50.0, 0.0);
432 ctx.place_float(left_frag, Float::Left, 0.0);
433
434 let right_frag = create_fragment(100.0, 80.0, 0.0);
435 ctx.place_float(right_frag, Float::Right, 0.0);
436
437 assert_eq!(ctx.clear(Clear::Both), 80.0);
439 }
440
441 #[test]
442 fn test_clear_all() {
443 let mut ctx = FloatContext::new(800.0);
444
445 let left_frag = create_fragment(100.0, 50.0, 0.0);
446 ctx.place_float(left_frag, Float::Left, 0.0);
447
448 let right_frag = create_fragment(100.0, 80.0, 0.0);
449 ctx.place_float(right_frag, Float::Right, 0.0);
450
451 assert_eq!(ctx.clear_all(), 80.0);
452 }
453
454 #[test]
455 fn test_float_respects_cursor_y() {
456 let mut ctx = FloatContext::new(800.0);
457 let frag = create_fragment(100.0, 50.0, 0.0);
458
459 let placed = ctx.place_float(frag, Float::Left, 100.0);
461
462 assert!(placed.position.y >= 100.0);
464 }
465
466 #[test]
467 fn test_float_with_margins() {
468 let mut ctx = FloatContext::new(800.0);
469 let frag = create_fragment(100.0, 50.0, 20.0);
470
471 let placed = ctx.place_float(frag, Float::Left, 0.0);
472
473 assert_eq!(placed.position.x, 20.0);
475 assert_eq!(placed.position.y, 20.0);
476
477 let margin_box = placed.margin_box();
479 assert_eq!(margin_box.x, 0.0);
480 assert_eq!(margin_box.y, 0.0);
481 }
482
483 #[test]
484 fn test_multiple_left_floats_stacking() {
485 let mut ctx = FloatContext::new(500.0);
486
487 let frag1 = create_fragment(100.0, 50.0, 0.0);
488 let placed1 = ctx.place_float(frag1, Float::Left, 0.0);
489 assert_eq!(placed1.position.x, 0.0);
490 assert_eq!(placed1.position.y, 0.0);
491
492 let frag2 = create_fragment(100.0, 50.0, 0.0);
493 let placed2 = ctx.place_float(frag2, Float::Left, 0.0);
494 assert_eq!(placed2.position.x, 0.0);
496 assert_eq!(placed2.position.y, 50.0);
497
498 let frag3 = create_fragment(100.0, 50.0, 0.0);
499 let placed3 = ctx.place_float(frag3, Float::Left, 0.0);
500 assert_eq!(placed3.position.x, 0.0);
502 assert_eq!(placed3.position.y, 100.0);
503 }
504
505 #[test]
506 fn test_float_below_previous_same_side() {
507 let mut ctx = FloatContext::new(800.0);
508
509 let frag1 = create_fragment(100.0, 50.0, 0.0);
511 ctx.place_float(frag1, Float::Left, 0.0);
512
513 let frag2 = create_fragment(100.0, 30.0, 0.0);
515 let placed2 = ctx.place_float(frag2, Float::Left, 0.0);
516
517 assert!(placed2.position.y >= 0.0);
519 }
520
521 #[test]
522 fn test_narrow_containing_block() {
523 let mut ctx = FloatContext::new(100.0);
524
525 let frag = create_fragment(150.0, 50.0, 0.0);
527 let placed = ctx.place_float(frag, Float::Left, 0.0);
528
529 assert!(placed.position.y > 100000.0);
532 assert_eq!(placed.position.x, 0.0);
533 }
534
535 #[test]
536 fn test_available_width_partial_overlap() {
537 let mut ctx = FloatContext::new(800.0);
538
539 let frag = create_fragment(200.0, 100.0, 0.0);
541 ctx.place_float(frag, Float::Left, 50.0);
542
543 let (left, width) = ctx.available_width_at(0.0, 100.0);
545 assert_eq!(left, 200.0); assert_eq!(width, 600.0);
547
548 let (left, width) = ctx.available_width_at(0.0, 40.0);
550 assert_eq!(left, 0.0); assert_eq!(width, 800.0);
552 }
553
554 #[test]
555 fn test_overlapping_left_and_right_floats() {
556 let mut ctx = FloatContext::new(800.0);
557
558 let left = create_fragment(350.0, 100.0, 0.0);
559 ctx.place_float(left, Float::Left, 0.0);
560
561 let right = create_fragment(350.0, 100.0, 0.0);
562 ctx.place_float(right, Float::Right, 0.0);
563
564 let (left_offset, width) = ctx.available_width_at(50.0, 10.0);
565 assert_eq!(left_offset, 350.0);
566 assert_eq!(width, 100.0); }
568
569 #[test]
570 fn test_zero_available_width() {
571 let mut ctx = FloatContext::new(400.0);
572
573 let left = create_fragment(250.0, 100.0, 0.0);
574 ctx.place_float(left, Float::Left, 0.0);
575
576 let right = create_fragment(250.0, 100.0, 0.0);
577 ctx.place_float(right, Float::Right, 0.0);
578
579 let (_, width) = ctx.available_width_at(50.0, 10.0);
580 assert_eq!(width, 150.0);
583 }
584}