Skip to main content

float_pigment_layout/
unit.rs

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