1use crate::*;
2use float_pigment_css::num_traits::{bounds::Bounded, Zero};
3
4#[allow(clippy::type_complexity)]
5pub(crate) struct LayoutUnit<T: LayoutTreeNode> {
6 pub(crate) cache: LayoutComputeCache<T::Length>,
7 pub(crate) result: Rect<T::Length>,
8 pub(crate) result_padding_rect: Rect<T::Length>,
9 pub(crate) result_content_rect: Rect<T::Length>,
10 pub(crate) computed_style: ComputedStyle<T::Length>,
11}
12
13impl<T: LayoutTreeNode> LayoutUnit<T> {
14 pub(crate) fn new() -> Self {
15 Self {
16 cache: LayoutComputeCache::new(),
17 result: Rect::zero(),
18 result_padding_rect: Rect::zero(),
19 result_content_rect: Rect::zero(),
20 computed_style: ComputedStyle::default(),
21 }
22 }
23
24 pub(crate) fn mark_dirty(&mut self, node_tree_visitor: &T::TreeVisitor) -> bool {
25 if !self.mark_self_dirty() {
26 return false;
27 }
28 let mut cur = node_tree_visitor;
29 while let Some(parent) = cur.parent() {
30 parent.tree_visitor().dirty_marked();
31 if !parent.layout_node().unit().mark_self_dirty() {
32 break;
33 }
34 cur = parent.tree_visitor();
35 }
36 true
37 }
38
39 fn mark_self_dirty(&mut self) -> bool {
40 self.cache.clear()
41 }
42
43 #[inline]
44 pub(crate) fn result(&self) -> Rect<T::Length> {
45 self.result
46 }
47
48 #[inline]
49 pub(crate) fn result_padding_rect(&self) -> Rect<T::Length> {
50 self.result_padding_rect
51 }
52
53 #[inline]
54 pub(crate) fn result_content_rect(&self) -> Rect<T::Length> {
55 self.result_content_rect
56 }
57
58 #[inline]
59 pub(crate) fn compute_with_containing_size(
60 &mut self,
61 env: &mut T::Env,
62 node: &T,
63 available_size: OptionSize<T::Length>,
64 containing_size: OptionSize<T::Length>,
65 ) {
66 let (margin, border, padding_border) = self.margin_border_padding(node, containing_size);
67 let min_max_limit =
68 self.normalized_min_max_limit(node, containing_size, border, padding_border);
69 let mut css_size = self.css_border_box_size(node, containing_size, border, padding_border);
70 css_size.width = css_size.width.or({
71 match node.style().display() {
72 Display::InlineBlock | Display::InlineFlex => OptionNum::none(),
73 _ => available_size.width - margin.left - margin.right,
74 }
75 });
76 css_size.height = css_size.height.or({
77 match node.style().display() {
78 Display::InlineBlock | Display::InlineFlex => OptionNum::none(),
79 _ => available_size.height - margin.top - margin.bottom,
80 }
81 });
82 let size = min_max_limit.normalized_size(css_size);
83 let req = ComputeRequest {
84 size,
85 parent_inner_size: size,
86 max_content: size,
87 kind: ComputeRequestKind::Position,
88 parent_is_block: node.style().display() == Display::Block
89 || node.style().display() == Display::InlineBlock
90 || node.style().display() == Display::Inline,
91 sizing_mode: SizingMode::Normal,
92 };
93 let result = self.compute_internal(env, node, req);
94 self.result = Rect::new(
95 Point::new(margin.left.or_zero(), result.collapsed_margin.start.solve()),
96 result.size.0,
97 );
98
99 self.computed_style.margin.top = result.collapsed_margin.start.solve();
101 self.computed_style.margin.left = margin.left.or_zero();
102 self.computed_style.margin.right = margin.right.or_zero();
103 self.computed_style.margin.bottom = result.collapsed_margin.end.solve();
104 }
105
106 #[inline]
107 pub(crate) fn compute(&mut self, env: &mut T::Env, node: &T, size: OptionSize<T::Length>) {
108 let size = Normalized(size);
109 let req = ComputeRequest {
110 size,
111 parent_inner_size: size,
112 max_content: size,
113 kind: ComputeRequestKind::Position,
114 parent_is_block: false,
115 sizing_mode: SizingMode::Normal,
116 };
117 let result = self.compute_internal(env, node, req);
118 self.result = Rect::new(Point::zero(), result.size.0);
119 }
120
121 #[inline]
122 pub(crate) fn computed_style(&self) -> ComputedStyle<T::Length> {
123 self.computed_style
124 }
125
126 pub(crate) fn clear_display_none_result(&mut self, node: &T) {
127 self.cache.touch(node);
129 self.cache.clear_position_cache();
130 self.result = Rect::zero();
131 self.result_padding_rect = Rect::zero();
132 self.result_content_rect = Rect::zero();
133 node.tree_visitor().for_each_child(|child_node, _| {
134 child_node
135 .layout_node()
136 .unit()
137 .clear_display_none_result(child_node);
138 });
139 }
140
141 pub(crate) fn compute_internal(
142 &mut self,
143 env: &mut T::Env,
144 node: &T,
145 request: ComputeRequest<T::Length>,
146 ) -> ComputeResult<T::Length> {
147 let ret = if let Some(r) = self.cache.read(node, &request) {
148 r
151 } else {
152 let style = node.style();
155 let (margin, border, padding_border) =
156 self.margin_border_padding(node, *request.parent_inner_size);
157 if let Some(ret) = self.compute_measure_block_if_exists(
158 env,
159 node,
160 request.clone(),
161 margin,
162 border,
163 padding_border,
164 ) {
165 ret
166 } else {
167 match style.display() {
168 Display::None => {
169 self.clear_display_none_result(node);
170 ComputeResult {
171 size: Normalized(Size::zero()),
172 first_baseline_ascent: Vector::zero(),
173 last_baseline_ascent: Vector::zero(),
174 collapsed_margin: CollapsedBlockMargin::zero(),
175 }
176 }
177 Display::Block | Display::InlineBlock | Display::Inline => {
178 algo::flow::Flow::compute(
179 self,
180 env,
181 node,
182 request.clone(),
183 margin,
184 border,
185 padding_border,
186 )
187 }
188 Display::Flex | Display::InlineFlex => algo::flex_box::FlexBox::compute(
189 self,
190 env,
191 node,
192 request.clone(),
193 margin,
194 border,
195 padding_border,
196 ),
197 Display::Grid | Display::InlineGrid => algo::grid::GridContainer::compute(
198 self,
199 env,
200 node,
201 request.clone(),
202 margin,
203 border,
204 padding_border,
205 ),
206 Display::FlowRoot => todo!(),
207 }
208 }
209 };
210
211 if request.kind == ComputeRequestKind::Position {
212 self.save_all_results(node, env, *request.parent_inner_size);
213 }
214 ret
216 }
217
218 pub(crate) fn save_all_results(
219 &mut self,
220 node: &T,
221 env: &mut T::Env,
222 parent_inner_size: OptionSize<T::Length>,
223 ) {
224 let (margin, border, padding_border) = self.margin_border_padding(node, parent_inner_size);
225 self.save_border_padding_result(border, padding_border);
226 self.save_computed_style(margin, border, padding_border - border);
227 node.size_updated(env, self.result.size, &self.computed_style);
228 }
229
230 pub(crate) fn save_computed_style(
231 &mut self,
232 margin: EdgeOption<T::Length>,
233 border: Edge<T::Length>,
234 padding: Edge<T::Length>,
235 ) {
236 self.computed_style = ComputedStyle {
237 margin: Edge {
238 left: margin.left.or_zero(),
239 right: margin.right.or_zero(),
240 top: margin.top.or_zero(),
241 bottom: margin.bottom.or_zero(),
242 },
243 border: Edge {
244 left: border.left,
245 right: border.right,
246 top: border.top,
247 bottom: border.bottom,
248 },
249 padding: Edge {
250 left: padding.left,
251 right: padding.right,
252 top: padding.top,
253 bottom: padding.bottom,
254 },
255 }
256 }
257
258 pub(crate) fn save_border_padding_result(
259 &mut self,
260 border: Edge<T::Length>,
261 padding_border: Edge<T::Length>,
262 ) {
263 self.result_padding_rect = Rect::new(
264 Point::new(border.left, border.top),
265 Size::new(
266 self.result.size.width - border.left - border.right,
267 self.result.size.height - border.top - border.bottom,
268 ),
269 );
270 self.result_content_rect = Rect::new(
271 Point::new(padding_border.left, padding_border.top),
272 Size::new(
273 self.result.size.width - padding_border.left - padding_border.right,
274 self.result.size.height - padding_border.top - padding_border.bottom,
275 ),
276 );
277 }
279
280 pub(crate) fn is_requested_size_fixed(
281 &mut self,
282 request: &ComputeRequest<T::Length>,
283 collapsed_margin: Option<CollapsedBlockMargin<T::Length>>,
284 ) -> Option<ComputeResult<T::Length>> {
285 let collapsed_margin = if let Some(x) = collapsed_margin {
286 x
287 } else if request.parent_is_block {
288 return None;
289 } else {
290 CollapsedBlockMargin::zero()
291 };
292 match request.kind {
294 ComputeRequestKind::AllSize => {
295 if let Some(width) = request.size.width.val() {
296 if let Some(height) = request.size.height.val() {
297 let size = Size::new(width, height);
298 return Some(ComputeResult {
299 size: Normalized(size),
300 first_baseline_ascent: size.to_vector(),
301 last_baseline_ascent: size.to_vector(),
302 collapsed_margin,
303 });
304 }
305 }
306 }
307 ComputeRequestKind::RowSize => {
308 if let Some(width) = request.size.width.val() {
309 let size = Size::new(width, T::Length::zero());
310 return Some(ComputeResult {
311 size: Normalized(size),
312 first_baseline_ascent: size.to_vector(),
313 last_baseline_ascent: size.to_vector(),
314 collapsed_margin,
315 });
316 }
317 }
318 ComputeRequestKind::ColSize => {
319 if let Some(height) = request.size.height.val() {
320 let size = Size::new(T::Length::zero(), height);
321 return Some(ComputeResult {
322 size: Normalized(size),
323 first_baseline_ascent: size.to_vector(),
324 last_baseline_ascent: size.to_vector(),
325 collapsed_margin,
326 });
327 }
328 }
329 _ => {}
330 }
331 None
332 }
333
334 #[allow(clippy::type_complexity)]
335 pub(crate) fn margin_border_padding(
336 &self,
337 node: &T,
338 parent_inner_size: OptionSize<T::Length>,
339 ) -> (EdgeOption<T::Length>, Edge<T::Length>, Edge<T::Length>) {
340 let style = node.style();
341 let length_ratio_base = match style.writing_mode() {
342 WritingMode::HorizontalTb => parent_inner_size.width,
343 WritingMode::VerticalLr | WritingMode::VerticalRl => parent_inner_size.height,
344 };
345 let margin = EdgeOption {
346 left: style
347 .margin_left()
348 .resolve_with_auto(length_ratio_base, node),
349 right: style
350 .margin_right()
351 .resolve_with_auto(length_ratio_base, node),
352 top: style
353 .margin_top()
354 .resolve_with_auto(length_ratio_base, node),
355 bottom: style
356 .margin_bottom()
357 .resolve_with_auto(length_ratio_base, node),
358 };
359 let border = Edge {
360 left: style
361 .border_left()
362 .resolve(length_ratio_base, node)
363 .or_zero(),
364 right: style
365 .border_right()
366 .resolve(length_ratio_base, node)
367 .or_zero(),
368 top: style
369 .border_top()
370 .resolve(length_ratio_base, node)
371 .or_zero(),
372 bottom: style
373 .border_bottom()
374 .resolve(length_ratio_base, node)
375 .or_zero(),
376 };
377 let padding = Edge {
378 left: style
379 .padding_left()
380 .resolve(length_ratio_base, node)
381 .or_zero(),
382 right: style
383 .padding_right()
384 .resolve(length_ratio_base, node)
385 .or_zero(),
386 top: style
387 .padding_top()
388 .resolve(length_ratio_base, node)
389 .or_zero(),
390 bottom: style
391 .padding_bottom()
392 .resolve(length_ratio_base, node)
393 .or_zero(),
394 };
395 let padding_border = Edge {
396 left: padding.left + border.left,
397 right: padding.right + border.right,
398 top: padding.top + border.top,
399 bottom: padding.bottom + border.bottom,
400 };
401 (margin, border, padding_border)
402 }
403
404 #[inline]
405 pub(crate) fn min_max_size_limit(
406 &self,
407 node: &T,
408 parent_inner_size: OptionSize<T::Length>,
409 size: Size<T::Length>,
410 border: Edge<T::Length>,
411 padding_border: Edge<T::Length>,
412 ) -> Normalized<Size<T::Length>> {
413 let ret = self.min_max_option_size_limit(
414 node,
415 parent_inner_size,
416 size_to_option(size),
417 border,
418 padding_border,
419 );
420 Normalized(Size::new(ret.width.or_zero(), ret.height.or_zero()))
421 }
422
423 #[inline]
424 pub(crate) fn min_max_option_size_limit(
425 &self,
426 node: &T,
427 parent_inner_size: OptionSize<T::Length>,
428 size: OptionSize<T::Length>,
429 border: Edge<T::Length>,
430 padding_border: Edge<T::Length>,
431 ) -> Normalized<OptionSize<T::Length>> {
432 let min_max_limit =
433 self.normalized_min_max_limit(node, parent_inner_size, border, padding_border);
434 min_max_limit.normalized_size(size)
435 }
436 #[inline]
437 pub(crate) fn min_max_size(
438 node: &T,
439 parent_size: OptionSize<T::Length>,
440 ) -> MinMaxSize<T::Length> {
441 let style = node.style();
442 let min_width = style.min_width().resolve(parent_size.width, node);
443 let max_width = style.max_width().resolve(parent_size.width, node);
444 let min_height = style.min_height().resolve(parent_size.height, node);
445 let max_height = style.max_height().resolve(parent_size.height, node);
446 MinMaxSize {
447 min_width,
448 max_width,
449 min_height,
450 max_height,
451 }
452 }
453
454 #[inline]
455 pub(crate) fn normalized_min_max_limit(
456 &self,
457 node: &T,
458 parent_size: OptionSize<T::Length>,
459 border: Edge<T::Length>,
460 padding_border: Edge<T::Length>,
461 ) -> MinMaxLimit<T::Length> {
462 let style = node.style();
463 let MinMaxSize {
464 min_width,
465 max_width,
466 min_height,
467 max_height,
468 } = Self::min_max_size(node, parent_size);
469 match style.box_sizing() {
470 BoxSizing::BorderBox => {
471 let min_width = padding_border.horizontal().maybe_max(min_width);
472 let min_height = padding_border.vertical().maybe_max(min_height);
473 MinMaxLimit {
474 min_width,
475 max_width,
476 min_height,
477 max_height,
478 }
479 }
480 BoxSizing::PaddingBox => {
481 let min_width = border.horizontal().maybe_max(min_width)
482 + padding_border.horizontal()
483 - border.horizontal();
484 let max_width = max_width + padding_border.horizontal() - border.horizontal();
485 let min_height = border.vertical().maybe_max(min_height)
486 + padding_border.vertical()
487 - border.vertical();
488 let max_height = max_height + padding_border.vertical() - border.vertical();
489 MinMaxLimit {
490 min_width,
491 max_width,
492 min_height,
493 max_height,
494 }
495 }
496 BoxSizing::ContentBox => {
497 let min_width =
498 T::Length::zero().maybe_max(min_width) + padding_border.horizontal();
499 let max_width = max_width + padding_border.horizontal();
500 let min_height =
501 T::Length::zero().maybe_max(min_height) + padding_border.vertical();
502 let max_height = max_height + padding_border.vertical();
503 MinMaxLimit {
504 min_width,
505 max_width,
506 min_height,
507 max_height,
508 }
509 }
510 }
511 }
512
513 #[inline]
514 pub(crate) fn css_border_box_size(
515 &self,
516 node: &T,
517 parent_inner_size: OptionSize<T::Length>,
518 border: Edge<T::Length>,
519 padding_border: Edge<T::Length>,
520 ) -> OptionSize<T::Length> {
521 let style = node.style();
522 let size = OptionSize::new(
523 style.width().resolve(parent_inner_size.width, node),
524 style.height().resolve(parent_inner_size.height, node),
525 );
526 match style.box_sizing() {
527 BoxSizing::BorderBox => size,
528 BoxSizing::PaddingBox => OptionSize::new(
529 size.width + padding_border.horizontal() - border.horizontal(),
530 size.height + padding_border.vertical() - border.vertical(),
531 ),
532 BoxSizing::ContentBox => OptionSize::new(
533 size.width + padding_border.horizontal(),
534 size.height + padding_border.vertical(),
535 ),
536 }
537 }
538
539 #[inline]
540 pub(crate) fn compute_measure_block_if_exists(
541 &mut self,
542 env: &mut T::Env,
543 node: &T,
544 request: ComputeRequest<T::Length>,
545 margin: EdgeOption<T::Length>,
546 border: Edge<T::Length>,
547 padding_border: Edge<T::Length>,
548 ) -> Option<ComputeResult<T::Length>> {
549 if node.should_measure(env) {
551 let req_size = match request.sizing_mode {
552 SizingMode::Normal => OptionSize::new(
553 request.size.width - padding_border.horizontal(),
554 request.size.height - padding_border.vertical(),
555 ),
556 SizingMode::MinContent => OptionSize::new(OptionNum::zero(), OptionNum::none()),
557 SizingMode::MaxContent => OptionSize::new(OptionNum::none(), OptionNum::none()),
558 };
559 let max_content = request.max_content;
560 let min_max_limit = self.normalized_min_max_limit(
561 node,
562 *request.parent_inner_size,
563 border,
564 padding_border,
565 );
566 let r = node.measure_block_size(
567 env,
568 req_size,
569 Size::new(
570 min_max_limit.min_width - padding_border.horizontal(),
571 min_max_limit.min_height - padding_border.vertical(),
572 ),
573 Size::new(
574 (min_max_limit.max_width - padding_border.horizontal())
575 .unwrap_or(T::Length::max_value()),
576 (min_max_limit.max_height - padding_border.vertical())
577 .unwrap_or(T::Length::max_value()),
578 ),
579 OptionSize::new(
580 max_content.width - padding_border.horizontal(),
581 max_content.height - padding_border.vertical(),
582 ),
583 request.kind == ComputeRequestKind::Position,
584 request.sizing_mode,
585 );
586 let size = Normalized(Size::new(
587 r.size.width + padding_border.horizontal(),
588 r.size.height + padding_border.vertical(),
589 ));
590 let first_baseline_ascent =
591 r.first_baseline_ascent + Vector::new(padding_border.left, padding_border.top);
592 let last_baseline_ascent =
593 r.last_baseline_ascent + Vector::new(padding_border.left, padding_border.top);
594 let axis_info = AxisInfo::from_writing_mode(node.style().writing_mode());
595 let ret = ComputeResult {
596 size,
597 first_baseline_ascent,
598 last_baseline_ascent,
599 collapsed_margin: CollapsedBlockMargin::from_margin(
600 margin
601 .or_zero()
602 .main_axis_start(axis_info.dir, axis_info.main_dir_rev),
603 margin
604 .or_zero()
605 .main_axis_end(axis_info.dir, axis_info.main_dir_rev),
606 ),
607 };
608 if request.kind == ComputeRequestKind::Position {
609 self.result = Rect::new(Point::zero(), *size);
610 self.cache.write_position(node, &request, ret);
611 } else {
612 self.cache.write_all_size(node, &request, ret);
613 }
614 Some(ret)
615 } else {
616 None
617 }
618 }
619
620 #[allow(clippy::too_many_arguments)]
621 pub(crate) fn get_measure_inline_unit_if_exists(
622 &mut self,
623 env: &mut T::Env,
624 node: &T,
625 parent_inner_size: OptionSize<T::Length>,
626 max_content: OptionSize<T::Length>,
627 border: Edge<T::Length>,
628 padding_border: Edge<T::Length>,
629 sizing_mode: SizingMode,
630 ) -> Option<T::InlineUnit> {
631 if node.should_measure(env) {
633 let css_box_size =
634 self.css_border_box_size(node, parent_inner_size, border, padding_border);
635 let req_size = match sizing_mode {
636 SizingMode::Normal => OptionSize::new(
637 css_box_size.width - padding_border.horizontal(),
638 css_box_size.height - padding_border.vertical(),
639 ),
640 SizingMode::MinContent => OptionSize::new(OptionNum::zero(), OptionNum::none()),
641 SizingMode::MaxContent => OptionSize::new(OptionNum::none(), OptionNum::none()),
642 };
643 let min_max_limit =
644 self.normalized_min_max_limit(node, parent_inner_size, border, padding_border);
645 let r = node.measure_inline_unit(
646 env,
647 req_size,
648 Size::new(
649 min_max_limit.min_width - padding_border.horizontal(),
650 min_max_limit.min_height - padding_border.vertical(),
651 ),
652 Size::new(
653 (min_max_limit.max_width - padding_border.horizontal())
654 .unwrap_or(T::Length::max_value()),
655 (min_max_limit.max_height - padding_border.vertical())
656 .unwrap_or(T::Length::max_value()),
657 ),
658 OptionSize::new(
659 max_content.width - padding_border.horizontal(),
660 max_content.height - padding_border.vertical(),
661 ),
662 sizing_mode,
663 );
664
665 let size = Size::new(
666 r.size.width + padding_border.horizontal(),
667 r.size.height + padding_border.vertical(),
668 );
669 let first_baseline_ascent =
670 r.first_baseline_ascent + Vector::new(padding_border.left, padding_border.top);
671 let last_baseline_ascent =
672 r.last_baseline_ascent + Vector::new(padding_border.left, padding_border.top);
673 let ret = T::InlineUnit::new(
674 env,
675 node,
676 MeasureResult {
677 size,
678 first_baseline_ascent,
679 last_baseline_ascent,
680 },
681 );
682 Some(ret)
683 } else {
684 None
685 }
686 }
687
688 #[inline]
689 pub(crate) fn gen_origin(
690 &mut self,
691 axis_info: AxisInfo,
692 parent_size: Size<T::Length>,
693 offset_main: T::Length,
694 offset_cross: T::Length,
695 ) -> Vector<T::Length> {
696 let (width, height, width_rev, height_rev) = match axis_info.dir {
697 AxisDirection::Horizontal => (
698 offset_main,
699 offset_cross,
700 axis_info.main_dir_rev,
701 axis_info.cross_dir_rev,
702 ),
703 AxisDirection::Vertical => (
704 offset_cross,
705 offset_main,
706 axis_info.cross_dir_rev,
707 axis_info.main_dir_rev,
708 ),
709 };
710 let width = match width_rev {
711 AxisReverse::NotReversed => width,
712 AxisReverse::Reversed => parent_size.width - width - self.result.size.width,
713 };
714 let height = match height_rev {
715 AxisReverse::NotReversed => height,
716 AxisReverse::Reversed => parent_size.height - height - self.result.size.height,
717 };
718 self.result.origin = Point::new(width, height);
719
720 self.result.origin.to_vector()
722 }
723}
724
725#[allow(missing_docs)]
726#[derive(Clone, PartialEq, Copy, Hash, Eq, Debug)]
728pub enum SizingMode {
729 Normal,
730 MinContent,
731 MaxContent,
732}
733
734#[derive(Clone, PartialEq)]
735pub(crate) struct ComputeRequest<L: LengthNum> {
736 pub(crate) size: Normalized<OptionSize<L>>, pub(crate) parent_inner_size: Normalized<OptionSize<L>>, pub(crate) max_content: Normalized<OptionSize<L>>, pub(crate) kind: ComputeRequestKind,
740 pub(crate) parent_is_block: bool,
741 pub(crate) sizing_mode: SizingMode,
742}
743
744impl<L: LengthNum> fmt::Debug for ComputeRequest<L> {
745 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
746 write!(
747 f,
748 "ComputeRequest<{:?},{:?}>({:?}x{:?}, parent {:?}x{:?}, max {:?}x{:?}, parent_is_block {:?})",
749 self.kind,
750 self.sizing_mode,
751 self.size.width,
752 self.size.height,
753 self.parent_inner_size.width,
754 self.parent_inner_size.height,
755 self.max_content.width,
756 self.max_content.height,
757 self.parent_is_block,
758 )
759 }
760}
761
762#[derive(Debug, Clone, Copy, PartialEq)]
763pub(crate) struct ComputeResult<L: LengthNum> {
764 pub(crate) size: Normalized<Size<L>>, pub(crate) first_baseline_ascent: Vector<L>, pub(crate) last_baseline_ascent: Vector<L>, pub(crate) collapsed_margin: CollapsedBlockMargin<L>, }
769
770#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
771pub(crate) enum ComputeRequestKind {
772 RowSize,
773 ColSize,
774 AllSize,
775 Position,
776}
777
778impl ComputeRequestKind {
779 pub(crate) fn shift_to_all_size(&self) -> Self {
780 match self {
781 Self::RowSize => Self::AllSize,
782 Self::ColSize => Self::AllSize,
783 Self::AllSize => Self::AllSize,
784 Self::Position => Self::Position,
785 }
786 }
787
788 pub(crate) fn shift_to_all_size_with_position(&self, with_position: bool) -> Self {
789 match self {
790 Self::RowSize => Self::AllSize,
791 Self::ColSize => Self::AllSize,
792 Self::AllSize => Self::AllSize,
793 Self::Position => {
794 if with_position {
795 Self::Position
796 } else {
797 Self::AllSize
798 }
799 }
800 }
801 }
802}
803
804#[derive(Debug, Clone, Copy, PartialEq)]
805pub(crate) struct CollapsedBlockMargin<L: LengthNum> {
806 pub(crate) collapsed_through: bool,
807 pub(crate) start: CollapsedMargin<L>,
808 pub(crate) end: CollapsedMargin<L>,
809}
810
811#[derive(Debug, Clone, Copy, PartialEq)]
812pub(crate) struct CollapsedMargin<L: LengthNum> {
813 positive: L,
814 negative: L,
815}
816
817impl<L: LengthNum> CollapsedBlockMargin<L> {
818 pub(crate) fn from_margin(margin_start: L, margin_end: L) -> Self {
819 Self {
820 collapsed_through: false,
821 start: CollapsedMargin::new(margin_start),
822 end: CollapsedMargin::new(margin_end),
823 }
824 }
825 pub(crate) fn from_collapsed_margin(
826 margin_start: CollapsedMargin<L>,
827 margin_end: CollapsedMargin<L>,
828 ) -> Self {
829 Self {
830 collapsed_through: false,
831 start: margin_start,
832 end: margin_end,
833 }
834 }
835 pub(crate) fn zero() -> Self {
836 Self {
837 collapsed_through: false,
838 start: CollapsedMargin::zero(),
839 end: CollapsedMargin::zero(),
840 }
841 }
842}
843
844impl<L: LengthNum> CollapsedMargin<L> {
845 pub(crate) fn zero() -> Self {
846 Self {
847 positive: L::zero(),
848 negative: L::zero(),
849 }
850 }
851 pub(crate) fn new(margin: L) -> Self {
852 Self {
853 positive: margin.max(L::zero()),
854 negative: margin.min(L::zero()),
855 }
856 }
857
858 pub(crate) fn adjoin(&self, other: &Self) -> Self {
859 Self {
860 positive: self.positive.max(other.positive),
861 negative: self.negative.min(other.negative),
862 }
863 }
864
865 pub(crate) fn adjoin_assign(&mut self, other: &Self) {
866 *self = self.adjoin(other);
867 }
868
869 pub(crate) fn solve(&self) -> L {
870 self.positive + self.negative
871 }
872}
873
874#[inline(always)]
875pub(crate) fn is_display_none<T: LayoutTreeNode>(style: &T::Style) -> bool {
876 style.display() == Display::None
877}