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}
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        // FIXME
100        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        // it is required to mark cache dirty here (although the cache is not used)
128        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            // if cached, use the cache value
149            // info!("!!! {:p} cache req {:?}", self, request);
150            r
151        } else {
152            // do request
153            // info!("!!! {:p} req {:?}", self, request);
154            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        // info!("!!! {:p} res {:?} {:?}", self, request, ret);
215        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        // info!("!!! {:p} rect padding {:?} content {:?}", self, self.result_padding_rect, self.result_content_rect);
278    }
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        // return if requested size is specified
293        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 the node has measure, accept the measure result
550        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 the node has measure, accept the measure result
632        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        // info!("!!! {:p} pos {:?}", self, self.result);
721        self.result.origin.to_vector()
722    }
723}
724
725#[allow(missing_docs)]
726/// SizingMode is used to determine the sizing mode of the node.
727#[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>>, // the expected size, margin excluded, but css width, height, and min max size considered
737    pub(crate) parent_inner_size: Normalized<OptionSize<L>>, // parent size without its padding and border, none represents auto
738    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
739    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>>, // only valid on corresponding size which the request includes
765    pub(crate) first_baseline_ascent: Vector<L>, // only valid on position request
766    pub(crate) last_baseline_ascent: Vector<L>, // only valid on position request
767    pub(crate) collapsed_margin: CollapsedBlockMargin<L>, // only valid on corresponding size which the request includes and collapsed_margin set
768}
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}