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