Skip to main content

inset_rendering/
table.rs

1//! Flutter counterpart: `rendering/table.dart`.
2//!
3//! The table's semantics (`describeSemanticsConfiguration`, `assembleSemanticsNode`) wait with
4//! the rest of accessibility.
5
6use std::any::{Any, TypeId};
7use std::collections::{HashMap, HashSet};
8use std::fmt;
9use std::rc::Rc;
10
11use inset_embedder::{Offset, Rect, Size, TextBaseline, TextDirection};
12use inset_foundation::{App, PRECISION_ERROR_TOLERANCE};
13use inset_painting::{BoxPainter, ClipContext, Decoration, ImageConfiguration};
14
15use crate::box_::{
16    AnyRenderBox, BoxConstraints, BoxHitTestResult, BoxParentData, DryLayoutFailure, RenderBox,
17    RenderBoxData,
18};
19use crate::object::{AnyRenderObject, ParentData, RenderHandle, RenderObject, RenderObjectData};
20use crate::painting_context::PaintingContext;
21use crate::table_border::TableBorder;
22
23/// Parent data used by [`RenderTable`] for its children.
24#[derive(Debug, Default)]
25pub struct TableCellParentData {
26    box_parent_data: BoxParentData,
27
28    /// Where this cell should be placed vertically.
29    ///
30    /// When using [`TableCellVerticalAlignment::Baseline`], the text baseline must be set as
31    /// well.
32    pub vertical_alignment: Option<TableCellVerticalAlignment>,
33
34    /// The column that the child was in the last time it was laid out.
35    pub x: Option<usize>,
36
37    /// The row that the child was in the last time it was laid out.
38    pub y: Option<usize>,
39}
40
41impl TableCellParentData {
42    /// Creates table cell parent data at offset zero, with the default vertical alignment.
43    pub const fn new() -> TableCellParentData {
44        TableCellParentData {
45            box_parent_data: BoxParentData::new(),
46            vertical_alignment: None,
47            x: None,
48            y: None,
49        }
50    }
51}
52
53impl ParentData for TableCellParentData {
54    fn provide(&self, id: TypeId) -> Option<&dyn Any> {
55        if id == TypeId::of::<TableCellParentData>() {
56            return Some(self);
57        }
58        self.box_parent_data.provide(id)
59    }
60
61    fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
62        if id == TypeId::of::<TableCellParentData>() {
63            return Some(self);
64        }
65        self.box_parent_data.provide_mut(id)
66    }
67}
68
69impl fmt::Display for TableCellParentData {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        fmt::Display::fmt(&self.box_parent_data, f)?;
72        match self.vertical_alignment {
73            None => f.write_str("; default vertical alignment"),
74            Some(vertical_alignment) => write!(f, "; {vertical_alignment:?}"),
75        }
76    }
77}
78
79/// A shared, immutable [`TableColumnWidth`] (a Dart object reference). Two references are the
80/// same column width only when they point at the same value, as Dart compares these objects.
81pub type TableColumnWidthRef = Rc<dyn TableColumnWidth>;
82
83/// Base class to describe how wide a column in a [`RenderTable`] should be.
84///
85/// To size a column to a specific number of pixels, use a [`FixedColumnWidth`]. This is the
86/// cheapest way to size a column.
87///
88/// Other algorithms that are relatively cheap include [`FlexColumnWidth`], which distributes
89/// the space equally among the flexible columns, [`FractionColumnWidth`], which sizes a column
90/// based on the size of the table's container.
91pub trait TableColumnWidth: fmt::Debug {
92    /// The smallest width that the column can have.
93    ///
94    /// The `cells` argument is an iterable that provides all the cells in the table for this
95    /// column. Walking the cells is by definition O(N), so algorithms that do that should be
96    /// considered expensive.
97    ///
98    /// The `container_width` argument is the `max_width` of the incoming constraints for the
99    /// table, and might be infinite.
100    fn min_intrinsic_width(
101        &self,
102        app: &mut App,
103        cells: &[AnyRenderBox],
104        container_width: f64,
105    ) -> f64;
106
107    /// The ideal width that the column should have. This must be equal to or greater than the
108    /// [`min_intrinsic_width`](Self::min_intrinsic_width). The column might be bigger than this
109    /// width, e.g. if the column is flexible or if the table's width ends up being forced to be
110    /// bigger than the sum of all the `max_intrinsic_width` values.
111    ///
112    /// The `cells` argument is an iterable that provides all the cells in the table for this
113    /// column. Walking the cells is by definition O(N), so algorithms that do that should be
114    /// considered expensive.
115    ///
116    /// The `container_width` argument is the `max_width` of the incoming constraints for the
117    /// table, and might be infinite.
118    fn max_intrinsic_width(
119        &self,
120        app: &mut App,
121        cells: &[AnyRenderBox],
122        container_width: f64,
123    ) -> f64;
124
125    /// The flex factor to apply to the cell if there is any room left over when laying out the
126    /// table. The remaining space is distributed to any columns with flex in proportion to
127    /// their flex value (higher values get more space).
128    ///
129    /// The `cells` argument is an iterable that provides all the cells in the table for this
130    /// column. Walking the cells is by definition O(N), so algorithms that do that should be
131    /// considered expensive.
132    fn flex(&self, app: &mut App, cells: &[AnyRenderBox]) -> Option<f64> {
133        let _ = (app, cells);
134        None
135    }
136
137    /// Downcast support for Dart's `is` checks on a column width.
138    fn as_any(&self) -> &dyn Any;
139}
140
141/// Sizes the column according to the intrinsic dimensions of all the cells in that column.
142///
143/// This is a very expensive way to size a column.
144///
145/// A flex value can be provided. If specified (and non-`None`), the column will participate in
146/// the distribution of remaining space once all the non-flexible columns have been sized.
147pub struct IntrinsicColumnWidth {
148    flex: Option<f64>,
149}
150
151impl IntrinsicColumnWidth {
152    /// Creates a column width based on intrinsic sizing.
153    ///
154    /// This sizing algorithm is very expensive.
155    ///
156    /// The `flex` argument specifies the flex factor to apply to the column if there is any
157    /// room left over when laying out the table. If `flex` is `None` (the default), the table
158    /// will not distribute any extra space to the column.
159    pub const fn new(flex: Option<f64>) -> IntrinsicColumnWidth {
160        IntrinsicColumnWidth { flex }
161    }
162}
163
164impl TableColumnWidth for IntrinsicColumnWidth {
165    fn min_intrinsic_width(
166        &self,
167        app: &mut App,
168        cells: &[AnyRenderBox],
169        _container_width: f64,
170    ) -> f64 {
171        let mut result: f64 = 0.0;
172        for cell in cells {
173            result = result.max(cell.get_min_intrinsic_width(app, f64::INFINITY));
174        }
175        result
176    }
177
178    fn max_intrinsic_width(
179        &self,
180        app: &mut App,
181        cells: &[AnyRenderBox],
182        _container_width: f64,
183    ) -> f64 {
184        let mut result: f64 = 0.0;
185        for cell in cells {
186            result = result.max(cell.get_max_intrinsic_width(app, f64::INFINITY));
187        }
188        result
189    }
190
191    fn flex(&self, _app: &mut App, _cells: &[AnyRenderBox]) -> Option<f64> {
192        self.flex
193    }
194
195    fn as_any(&self) -> &dyn Any {
196        self
197    }
198}
199
200impl fmt::Debug for IntrinsicColumnWidth {
201    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202        match self.flex {
203            Some(flex) => write!(f, "IntrinsicColumnWidth(flex: {flex:.1})"),
204            None => f.write_str("IntrinsicColumnWidth(flex: null)"),
205        }
206    }
207}
208
209/// Sizes the column to a specific number of pixels.
210///
211/// This is the cheapest way to size a column.
212pub struct FixedColumnWidth {
213    /// The width the column should occupy in logical pixels.
214    pub value: f64,
215}
216
217impl FixedColumnWidth {
218    /// Creates a column width based on a fixed number of logical pixels.
219    pub const fn new(value: f64) -> FixedColumnWidth {
220        FixedColumnWidth { value }
221    }
222}
223
224impl TableColumnWidth for FixedColumnWidth {
225    fn min_intrinsic_width(
226        &self,
227        _app: &mut App,
228        _cells: &[AnyRenderBox],
229        _container_width: f64,
230    ) -> f64 {
231        self.value
232    }
233
234    fn max_intrinsic_width(
235        &self,
236        _app: &mut App,
237        _cells: &[AnyRenderBox],
238        _container_width: f64,
239    ) -> f64 {
240        self.value
241    }
242
243    fn as_any(&self) -> &dyn Any {
244        self
245    }
246}
247
248impl fmt::Debug for FixedColumnWidth {
249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        write!(f, "FixedColumnWidth({:.1})", self.value)
251    }
252}
253
254/// Sizes the column to a fraction of the table's constraints' `max_width`.
255///
256/// This is a cheap way to size a column.
257pub struct FractionColumnWidth {
258    /// The fraction of the table's constraints' `max_width` that this column should occupy.
259    pub value: f64,
260}
261
262impl FractionColumnWidth {
263    /// Creates a column width based on a fraction of the table's constraints' `max_width`.
264    pub const fn new(value: f64) -> FractionColumnWidth {
265        FractionColumnWidth { value }
266    }
267}
268
269impl TableColumnWidth for FractionColumnWidth {
270    fn min_intrinsic_width(
271        &self,
272        _app: &mut App,
273        _cells: &[AnyRenderBox],
274        container_width: f64,
275    ) -> f64 {
276        if !container_width.is_finite() {
277            return 0.0;
278        }
279        self.value * container_width
280    }
281
282    fn max_intrinsic_width(
283        &self,
284        _app: &mut App,
285        _cells: &[AnyRenderBox],
286        container_width: f64,
287    ) -> f64 {
288        if !container_width.is_finite() {
289            return 0.0;
290        }
291        self.value * container_width
292    }
293
294    fn as_any(&self) -> &dyn Any {
295        self
296    }
297}
298
299impl fmt::Debug for FractionColumnWidth {
300    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301        write!(f, "FractionColumnWidth({})", self.value)
302    }
303}
304
305/// Sizes the column by taking a part of the remaining space once all the other columns have
306/// been laid out.
307///
308/// For example, if two columns have a [`FlexColumnWidth`], then half the space will go to one
309/// and half the space will go to the other.
310///
311/// This is a cheap way to size a column.
312pub struct FlexColumnWidth {
313    /// The fraction of the remaining space once all the other columns have been laid out that
314    /// this column should occupy.
315    pub value: f64,
316}
317
318thread_local! {
319    static DEFAULT_FLEX_COLUMN_WIDTH: TableColumnWidthRef = Rc::new(FlexColumnWidth::new(1.0));
320}
321
322impl FlexColumnWidth {
323    /// Creates a column width based on a fraction of the remaining space once all the other
324    /// columns have been laid out. Dart's default `value` is 1.0.
325    pub const fn new(value: f64) -> FlexColumnWidth {
326        FlexColumnWidth { value }
327    }
328
329    /// Dart's `const FlexColumnWidth()`, the default column width of a table: one shared value,
330    /// as Dart's constant is one object, so a table given the default again is unchanged.
331    pub fn default_width() -> TableColumnWidthRef {
332        DEFAULT_FLEX_COLUMN_WIDTH.with(Rc::clone)
333    }
334}
335
336impl TableColumnWidth for FlexColumnWidth {
337    fn min_intrinsic_width(
338        &self,
339        _app: &mut App,
340        _cells: &[AnyRenderBox],
341        _container_width: f64,
342    ) -> f64 {
343        0.0
344    }
345
346    fn max_intrinsic_width(
347        &self,
348        _app: &mut App,
349        _cells: &[AnyRenderBox],
350        _container_width: f64,
351    ) -> f64 {
352        0.0
353    }
354
355    fn flex(&self, _app: &mut App, _cells: &[AnyRenderBox]) -> Option<f64> {
356        Some(self.value)
357    }
358
359    fn as_any(&self) -> &dyn Any {
360        self
361    }
362}
363
364impl fmt::Debug for FlexColumnWidth {
365    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366        write!(f, "FlexColumnWidth({:.1})", self.value)
367    }
368}
369
370/// Sizes the column such that it is the size that is the maximum of two column width
371/// specifications.
372///
373/// For example, to have a column be 10% of the container width or 100px, whichever is bigger,
374/// you could use:
375///
376/// ```ignore
377/// MaxColumnWidth::new(Rc::new(FixedColumnWidth::new(100.0)), Rc::new(FractionColumnWidth::new(0.1)))
378/// ```
379///
380/// Both specifications are evaluated, so if either specification is expensive, so is this.
381#[derive(Debug)]
382pub struct MaxColumnWidth {
383    /// A lower bound for the width of this column.
384    pub a: TableColumnWidthRef,
385    /// Another lower bound for the width of this column.
386    pub b: TableColumnWidthRef,
387}
388
389impl MaxColumnWidth {
390    /// Creates a column width that is the maximum of two other column widths.
391    pub fn new(a: TableColumnWidthRef, b: TableColumnWidthRef) -> MaxColumnWidth {
392        MaxColumnWidth { a, b }
393    }
394}
395
396impl TableColumnWidth for MaxColumnWidth {
397    fn min_intrinsic_width(
398        &self,
399        app: &mut App,
400        cells: &[AnyRenderBox],
401        container_width: f64,
402    ) -> f64 {
403        self.a
404            .min_intrinsic_width(app, cells, container_width)
405            .max(self.b.min_intrinsic_width(app, cells, container_width))
406    }
407
408    fn max_intrinsic_width(
409        &self,
410        app: &mut App,
411        cells: &[AnyRenderBox],
412        container_width: f64,
413    ) -> f64 {
414        self.a
415            .max_intrinsic_width(app, cells, container_width)
416            .max(self.b.max_intrinsic_width(app, cells, container_width))
417    }
418
419    fn flex(&self, app: &mut App, cells: &[AnyRenderBox]) -> Option<f64> {
420        let a_flex = self.a.flex(app, cells);
421        let b_flex = self.b.flex(app, cells);
422        match (a_flex, b_flex) {
423            (None, _) => b_flex,
424            (Some(_), None) => a_flex,
425            (Some(a_flex), Some(b_flex)) => Some(a_flex.max(b_flex)),
426        }
427    }
428
429    fn as_any(&self) -> &dyn Any {
430        self
431    }
432}
433
434/// Sizes the column such that it is the size that is the minimum of two column width
435/// specifications.
436///
437/// For example, to have a column be 10% of the container width but never bigger than 100px,
438/// you could use:
439///
440/// ```ignore
441/// MinColumnWidth::new(Rc::new(FixedColumnWidth::new(100.0)), Rc::new(FractionColumnWidth::new(0.1)))
442/// ```
443///
444/// Both specifications are evaluated, so if either specification is expensive, so is this.
445#[derive(Debug)]
446pub struct MinColumnWidth {
447    /// An upper bound for the width of this column.
448    pub a: TableColumnWidthRef,
449    /// Another upper bound for the width of this column.
450    pub b: TableColumnWidthRef,
451}
452
453impl MinColumnWidth {
454    /// Creates a column width that is the minimum of two other column widths.
455    pub fn new(a: TableColumnWidthRef, b: TableColumnWidthRef) -> MinColumnWidth {
456        MinColumnWidth { a, b }
457    }
458}
459
460impl TableColumnWidth for MinColumnWidth {
461    fn min_intrinsic_width(
462        &self,
463        app: &mut App,
464        cells: &[AnyRenderBox],
465        container_width: f64,
466    ) -> f64 {
467        self.a
468            .min_intrinsic_width(app, cells, container_width)
469            .min(self.b.min_intrinsic_width(app, cells, container_width))
470    }
471
472    fn max_intrinsic_width(
473        &self,
474        app: &mut App,
475        cells: &[AnyRenderBox],
476        container_width: f64,
477    ) -> f64 {
478        self.a
479            .max_intrinsic_width(app, cells, container_width)
480            .min(self.b.max_intrinsic_width(app, cells, container_width))
481    }
482
483    fn flex(&self, app: &mut App, cells: &[AnyRenderBox]) -> Option<f64> {
484        let a_flex = self.a.flex(app, cells);
485        let b_flex = self.b.flex(app, cells);
486        match (a_flex, b_flex) {
487            (None, _) => b_flex,
488            (Some(_), None) => a_flex,
489            (Some(a_flex), Some(b_flex)) => Some(a_flex.min(b_flex)),
490        }
491    }
492
493    fn as_any(&self) -> &dyn Any {
494        self
495    }
496}
497
498/// Vertical alignment options for cells in [`RenderTable`] objects.
499///
500/// This is specified using [`TableCellParentData`] objects on the parent data of the children
501/// of the [`RenderTable`].
502#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
503pub enum TableCellVerticalAlignment {
504    /// Cells with this alignment are placed with their top at the top of the row.
505    #[default]
506    Top,
507
508    /// Cells with this alignment are vertically centered in the row.
509    Middle,
510
511    /// Cells with this alignment are placed with their bottom at the bottom of the row.
512    Bottom,
513
514    /// Cells with this alignment are aligned such that they all share the same baseline. Cells
515    /// with no baseline are top-aligned instead. The baseline used is specified by
516    /// [`RenderTable::text_baseline`]. It is not valid to use the baseline value if
517    /// [`RenderTable::text_baseline`] is not specified.
518    ///
519    /// This vertical alignment is relatively expensive because it causes the table to compute
520    /// the baseline for each cell in the row.
521    Baseline,
522
523    /// Cells with this alignment are sized to be as tall as the row, then made to fit the row.
524    /// If all the cells have this alignment, then the row will have zero height.
525    Fill,
526
527    /// Cells with this alignment are sized to be the same height as the tallest cell in the
528    /// row.
529    IntrinsicHeight,
530}
531
532/// A table where the columns and rows are sized to fit the contents of the cells.
533pub struct RenderTable {
534    render_object: RenderObjectData,
535    render_box: RenderBoxData,
536    // Children are stored in row-major order.
537    // children.len() must be rows * columns
538    children: Vec<Option<AnyRenderBox>>,
539    columns: usize,
540    rows: usize,
541    column_widths: HashMap<usize, TableColumnWidthRef>,
542    default_column_width: TableColumnWidthRef,
543    text_direction: TextDirection,
544    border: Option<TableBorder>,
545    // row_decorations and row_decoration_painters need to be in sync. They have to either both
546    // be None or have same length.
547    row_decorations: Option<Vec<Option<Box<dyn Decoration>>>>,
548    row_decoration_painters: Option<Vec<Option<Box<dyn BoxPainter>>>>,
549    configuration: ImageConfiguration,
550    default_vertical_alignment: TableCellVerticalAlignment,
551    text_baseline: Option<TextBaseline>,
552    // cache the table geometry for painting purposes
553    row_tops: Vec<f64>,
554    column_lefts: Option<Vec<f64>>,
555    table_width: f64,
556    baseline_distance: Option<f64>,
557}
558
559impl RenderTable {
560    /// Creates a table render object with no rows or columns.
561    ///
562    /// Dart's `columns`, `rows` and `children` arguments are [`set_columns`](Self::set_columns),
563    /// [`set_rows`](Self::set_rows) and [`set_children`](Self::set_children), and its other named
564    /// arguments are the setters: [`set_column_widths`](Self::set_column_widths) (empty),
565    /// [`set_default_column_width`](Self::set_default_column_width)
566    /// ([`FlexColumnWidth::default_width`]), [`set_border`](Self::set_border),
567    /// [`set_row_decorations`](Self::set_row_decorations),
568    /// [`set_configuration`](Self::set_configuration) ([`ImageConfiguration::EMPTY`]),
569    /// [`set_default_vertical_alignment`](Self::set_default_vertical_alignment)
570    /// ([`TableCellVerticalAlignment::Top`]) and [`set_text_baseline`](Self::set_text_baseline).
571    pub fn new(app: &mut App, text_direction: TextDirection) -> RenderHandle<RenderTable> {
572        RenderHandle::new_box(
573            app,
574            RenderTable {
575                render_object: RenderObjectData::new(),
576                render_box: RenderBoxData::new(),
577                children: Vec::new(),
578                columns: 0,
579                rows: 0,
580                column_widths: HashMap::new(),
581                default_column_width: FlexColumnWidth::default_width(),
582                text_direction,
583                border: None,
584                row_decorations: None,
585                row_decoration_painters: None,
586                configuration: ImageConfiguration::EMPTY,
587                default_vertical_alignment: TableCellVerticalAlignment::Top,
588                text_baseline: None,
589                row_tops: Vec::new(),
590                column_lefts: None,
591                table_width: 0.0,
592                baseline_distance: None,
593            },
594        )
595    }
596
597    /// The number of vertical alignment lines in this table.
598    ///
599    /// Changing the number of columns will remove any children that no longer fit in the
600    /// table.
601    ///
602    /// Changing the number of columns is an expensive operation because the table needs to
603    /// rearrange its internal representation.
604    pub fn columns(self: RenderHandle<Self>, app: &App) -> usize {
605        self.get(app).columns
606    }
607
608    /// Sets [`columns`](Self::columns).
609    pub fn set_columns(self: RenderHandle<Self>, app: &mut App, value: usize) {
610        if value == self.columns(app) {
611            return;
612        }
613        let old_columns = self.columns(app);
614        let rows = self.rows(app);
615        let this = self.get_mut(app);
616        let old_children = std::mem::take(&mut this.children);
617        this.columns = value;
618        let columns = value;
619        this.children = vec![None; columns * rows];
620        let columns_to_copy = columns.min(old_columns);
621        for y in 0..rows {
622            for x in 0..columns_to_copy {
623                this.children[x + y * columns] = old_children[x + y * old_columns];
624            }
625        }
626        if old_columns > columns {
627            for y in 0..rows {
628                for x in columns..old_columns {
629                    let xy = x + y * old_columns;
630                    if let Some(old_child) = old_children[xy] {
631                        self.drop_child(app, old_child.as_object());
632                    }
633                }
634            }
635        }
636        self.mark_needs_layout(app);
637    }
638
639    /// The number of horizontal alignment lines in this table.
640    ///
641    /// Changing the number of rows will remove any children that no longer fit in the table.
642    pub fn rows(self: RenderHandle<Self>, app: &App) -> usize {
643        self.get(app).rows
644    }
645
646    /// Sets [`rows`](Self::rows).
647    pub fn set_rows(self: RenderHandle<Self>, app: &mut App, value: usize) {
648        if value == self.rows(app) {
649            return;
650        }
651        let columns = self.columns(app);
652        if self.get(app).rows > value {
653            for xy in columns * value..self.get(app).children.len() {
654                if let Some(child) = self.get(app).children[xy] {
655                    self.drop_child(app, child.as_object());
656                }
657            }
658        }
659        let this = self.get_mut(app);
660        this.rows = value;
661        this.children.resize(columns * value, None);
662        self.mark_needs_layout(app);
663    }
664
665    /// How the horizontal extents of the columns of this table should be determined.
666    ///
667    /// If the map has no entry for a given column, the table uses the
668    /// [`default_column_width`](Self::default_column_width) instead.
669    ///
670    /// The layout performance of the table depends critically on which column sizing
671    /// algorithms are used here. In particular, [`IntrinsicColumnWidth`] is quite expensive
672    /// because it needs to measure each cell in the column to determine the intrinsic size of
673    /// the column.
674    ///
675    /// This property can never return `None`. If it is set to `None`, and the existing map is
676    /// not empty, then the value is replaced by an empty map. (If it is set to `None` while the
677    /// current value is an empty map, the value is not changed.)
678    pub fn column_widths(
679        self: RenderHandle<Self>,
680        app: &App,
681    ) -> HashMap<usize, TableColumnWidthRef> {
682        self.get(app).column_widths.clone()
683    }
684
685    /// Sets [`column_widths`](Self::column_widths).
686    pub fn set_column_widths(
687        self: RenderHandle<Self>,
688        app: &mut App,
689        value: Option<HashMap<usize, TableColumnWidthRef>>,
690    ) {
691        if self.get(app).column_widths.is_empty() && value.is_none() {
692            return;
693        }
694        self.get_mut(app).column_widths = value.unwrap_or_default();
695        self.mark_needs_layout(app);
696    }
697
698    /// Determines how the width of column with the given index is determined.
699    pub fn set_column_width(
700        self: RenderHandle<Self>,
701        app: &mut App,
702        column: usize,
703        value: TableColumnWidthRef,
704    ) {
705        if self
706            .get(app)
707            .column_widths
708            .get(&column)
709            .is_some_and(|current| Rc::ptr_eq(current, &value))
710        {
711            return;
712        }
713        self.get_mut(app).column_widths.insert(column, value);
714        self.mark_needs_layout(app);
715    }
716
717    /// How to determine with widths of columns that don't have an explicit sizing algorithm.
718    ///
719    /// Specifically, the [`default_column_width`](Self::default_column_width) is used for
720    /// column `i` if `column_widths[i]` is absent.
721    pub fn default_column_width(self: RenderHandle<Self>, app: &App) -> TableColumnWidthRef {
722        Rc::clone(&self.get(app).default_column_width)
723    }
724
725    /// Sets [`default_column_width`](Self::default_column_width).
726    pub fn set_default_column_width(
727        self: RenderHandle<Self>,
728        app: &mut App,
729        value: TableColumnWidthRef,
730    ) {
731        if Rc::ptr_eq(&self.get(app).default_column_width, &value) {
732            return;
733        }
734        self.get_mut(app).default_column_width = value;
735        self.mark_needs_layout(app);
736    }
737
738    /// The direction in which the columns are ordered.
739    pub fn text_direction(self: RenderHandle<Self>, app: &App) -> TextDirection {
740        self.get(app).text_direction
741    }
742
743    /// Sets [`text_direction`](Self::text_direction).
744    pub fn set_text_direction(self: RenderHandle<Self>, app: &mut App, value: TextDirection) {
745        if self.get(app).text_direction == value {
746            return;
747        }
748        self.get_mut(app).text_direction = value;
749        self.mark_needs_layout(app);
750    }
751
752    /// The style to use when painting the boundary and interior divisions of the table.
753    pub fn border(self: RenderHandle<Self>, app: &App) -> Option<TableBorder> {
754        self.get(app).border
755    }
756
757    /// Sets [`border`](Self::border).
758    pub fn set_border(self: RenderHandle<Self>, app: &mut App, value: Option<TableBorder>) {
759        if self.border(app) == value {
760            return;
761        }
762        self.get_mut(app).border = value;
763        self.mark_needs_paint(app);
764    }
765
766    /// The decorations to use for each row of the table.
767    ///
768    /// Row decorations fill the horizontal and vertical extent of each row in the table, unlike
769    /// decorations for individual cells, which might not fill either.
770    pub fn row_decorations(self: RenderHandle<Self>, app: &App) -> &[Option<Box<dyn Decoration>>] {
771        self.get(app).row_decorations.as_deref().unwrap_or(&[])
772    }
773
774    /// Sets [`row_decorations`](Self::row_decorations).
775    pub fn set_row_decorations(
776        self: RenderHandle<Self>,
777        app: &mut App,
778        value: Option<Vec<Option<Box<dyn Decoration>>>>,
779    ) {
780        let this = self.get_mut(app);
781        this.row_decorations = value;
782        if let Some(painters) = this.row_decoration_painters.take() {
783            for mut painter in painters.into_iter().flatten() {
784                painter.dispose();
785            }
786        }
787        this.row_decoration_painters = this
788            .row_decorations
789            .as_ref()
790            .map(|decorations| decorations.iter().map(|_| None).collect());
791    }
792
793    /// The settings to pass to the [`row_decorations`](Self::row_decorations) when painting, so
794    /// that they can resolve images appropriately. See [`ImageConfiguration`].
795    pub fn configuration(self: RenderHandle<Self>, app: &App) -> &ImageConfiguration {
796        &self.get(app).configuration
797    }
798
799    /// Sets [`configuration`](Self::configuration).
800    pub fn set_configuration(self: RenderHandle<Self>, app: &mut App, value: ImageConfiguration) {
801        if value == self.get(app).configuration {
802            return;
803        }
804        self.get_mut(app).configuration = value;
805        self.mark_needs_paint(app);
806    }
807
808    /// How cells that do not explicitly specify a vertical alignment are aligned vertically.
809    pub fn default_vertical_alignment(
810        self: RenderHandle<Self>,
811        app: &App,
812    ) -> TableCellVerticalAlignment {
813        self.get(app).default_vertical_alignment
814    }
815
816    /// Sets [`default_vertical_alignment`](Self::default_vertical_alignment).
817    pub fn set_default_vertical_alignment(
818        self: RenderHandle<Self>,
819        app: &mut App,
820        value: TableCellVerticalAlignment,
821    ) {
822        if self.get(app).default_vertical_alignment == value {
823            return;
824        }
825        self.get_mut(app).default_vertical_alignment = value;
826        self.mark_needs_layout(app);
827    }
828
829    /// The text baseline to use when aligning rows using
830    /// [`TableCellVerticalAlignment::Baseline`].
831    pub fn text_baseline(self: RenderHandle<Self>, app: &App) -> Option<TextBaseline> {
832        self.get(app).text_baseline
833    }
834
835    /// Sets [`text_baseline`](Self::text_baseline).
836    pub fn set_text_baseline(self: RenderHandle<Self>, app: &mut App, value: Option<TextBaseline>) {
837        if self.get(app).text_baseline == value {
838            return;
839        }
840        self.get_mut(app).text_baseline = value;
841        self.mark_needs_layout(app);
842    }
843
844    /// Replaces the children of this table with the given cells.
845    ///
846    /// The cells are divided into the specified number of columns before replacing the
847    /// existing children.
848    ///
849    /// If the new cells contain any existing children of the table, those children are moved
850    /// to their new location in the table rather than removed from the table and re-added.
851    pub fn set_flat_children(
852        self: RenderHandle<Self>,
853        app: &mut App,
854        columns: usize,
855        cells: Vec<Option<AnyRenderBox>>,
856    ) {
857        // consider the case of a newly empty table
858        if columns == 0 || cells.is_empty() {
859            debug_assert!(cells.is_empty());
860            self.get_mut(app).columns = columns;
861            if self.get(app).children.is_empty() {
862                debug_assert_eq!(self.get(app).rows, 0);
863                return;
864            }
865            let old_children = std::mem::take(&mut self.get_mut(app).children);
866            for old_child in old_children.into_iter().flatten() {
867                self.drop_child(app, old_child.as_object());
868            }
869            self.get_mut(app).rows = 0;
870            self.mark_needs_layout(app);
871            return;
872        }
873        debug_assert!(cells.len().is_multiple_of(columns));
874        let (old_columns, old_rows) = (self.get(app).columns, self.get(app).rows);
875        // fill a set with the cells that are moving (it's important not to drop_child a child
876        // that's remaining with us, because that would clear their parent data field)
877        let mut lost_children: HashSet<AnyRenderBox> = HashSet::new();
878        for y in 0..old_rows {
879            for x in 0..old_columns {
880                let xy_old = x + y * old_columns;
881                let xy_new = x + y * columns;
882                if let Some(old_child) = self.get(app).children[xy_old]
883                    && (x >= columns || xy_new >= cells.len() || Some(old_child) != cells[xy_new])
884                {
885                    lost_children.insert(old_child);
886                }
887            }
888        }
889        // adopt cells that are arriving, and cross cells that are just moving off our list of
890        // lost_children
891        let mut y = 0;
892        while y * columns < cells.len() {
893            for x in 0..columns {
894                let xy_new = x + y * columns;
895                let xy_old = x + y * old_columns;
896                if let Some(cell) = cells[xy_new]
897                    && (x >= old_columns
898                        || y >= old_rows
899                        || self.get(app).children[xy_old] != Some(cell))
900                    && !lost_children.remove(&cell)
901                {
902                    self.adopt_child(app, cell.as_object());
903                }
904            }
905            y += 1;
906        }
907        // drop all the lost children
908        for child in lost_children {
909            self.drop_child(app, child.as_object());
910        }
911        // update our internal values
912        let this = self.get_mut(app);
913        this.columns = columns;
914        this.rows = cells.len() / columns;
915        this.children = cells;
916        debug_assert_eq!(this.children.len(), this.rows * this.columns);
917        self.mark_needs_layout(app);
918    }
919
920    /// Replaces the children of this table with the given cells.
921    pub fn set_children(
922        self: RenderHandle<Self>,
923        app: &mut App,
924        cells: Option<Vec<Vec<AnyRenderBox>>>,
925    ) {
926        // TODO(ianh): Make this smarter, like set_flat_children
927        let Some(cells) = cells else {
928            self.set_flat_children(app, 0, Vec::new());
929            return;
930        };
931        let old_children = std::mem::take(&mut self.get_mut(app).children);
932        for old_child in old_children.into_iter().flatten() {
933            self.drop_child(app, old_child.as_object());
934        }
935        let this = self.get_mut(app);
936        this.columns = cells.first().map_or(0, Vec::len);
937        this.rows = 0;
938        for row in cells {
939            self.add_row(app, row.into_iter().map(Some).collect());
940        }
941        debug_assert_eq!(
942            self.get(app).children.len(),
943            self.rows(app) * self.columns(app)
944        );
945    }
946
947    /// Adds a row to the end of the table.
948    ///
949    /// The newly added children must not already have parents.
950    pub fn add_row(self: RenderHandle<Self>, app: &mut App, cells: Vec<Option<AnyRenderBox>>) {
951        debug_assert_eq!(cells.len(), self.columns(app));
952        debug_assert_eq!(
953            self.get(app).children.len(),
954            self.rows(app) * self.columns(app)
955        );
956        let this = self.get_mut(app);
957        this.rows += 1;
958        this.children.extend(cells.iter().copied());
959        for cell in cells.into_iter().flatten() {
960            self.adopt_child(app, cell.as_object());
961        }
962        self.mark_needs_layout(app);
963    }
964
965    /// Replaces the child at the given position with the given child.
966    ///
967    /// If the given child is already located at the given position, this function does not
968    /// modify the table. Otherwise, the given child must not already have a parent.
969    pub fn set_child(
970        self: RenderHandle<Self>,
971        app: &mut App,
972        x: usize,
973        y: usize,
974        value: Option<AnyRenderBox>,
975    ) {
976        debug_assert!(x < self.columns(app) && y < self.rows(app));
977        debug_assert_eq!(
978            self.get(app).children.len(),
979            self.rows(app) * self.columns(app)
980        );
981        let xy = x + y * self.columns(app);
982        let old_child = self.get(app).children[xy];
983        if old_child == value {
984            return;
985        }
986        if let Some(old_child) = old_child {
987            self.drop_child(app, old_child.as_object());
988        }
989        self.get_mut(app).children[xy] = value;
990        if let Some(value) = value {
991            self.adopt_child(app, value.as_object());
992        }
993    }
994
995    /// Returns the list of [`RenderBox`] objects that are in the given column, in row order,
996    /// starting from the first row.
997    pub fn column(self: RenderHandle<Self>, app: &App, x: usize) -> Vec<AnyRenderBox> {
998        let this = self.get(app);
999        (0..this.rows)
1000            .filter_map(|y| this.children[x + y * this.columns])
1001            .collect()
1002    }
1003
1004    /// Returns the list of [`RenderBox`] objects that are on the given row, in column order,
1005    /// starting with the first column.
1006    pub fn row(self: RenderHandle<Self>, app: &App, y: usize) -> Vec<AnyRenderBox> {
1007        let this = self.get(app);
1008        let start = y * this.columns;
1009        let end = (y + 1) * this.columns;
1010        this.children[start..end]
1011            .iter()
1012            .flatten()
1013            .copied()
1014            .collect()
1015    }
1016
1017    /// The column width the table uses for column `x`.
1018    fn column_width_for(self: RenderHandle<Self>, app: &App, x: usize) -> TableColumnWidthRef {
1019        let this = self.get(app);
1020        Rc::clone(
1021            this.column_widths
1022                .get(&x)
1023                .unwrap_or(&this.default_column_width),
1024        )
1025    }
1026
1027    fn compute_column_widths(
1028        self: RenderHandle<Self>,
1029        app: &mut App,
1030        constraints: BoxConstraints,
1031    ) -> Vec<f64> {
1032        let columns = self.columns(app);
1033        debug_assert_eq!(self.get(app).children.len(), self.rows(app) * columns);
1034        // We apply the constraints to the column widths in the order of
1035        // least important to most important:
1036        // 1. apply the ideal widths (max_intrinsic_width)
1037        // 2. grow the flex columns so that the table has the max_width (if
1038        //    finite) or the min_width (if not)
1039        // 3. if there were no flex columns, then grow the table to the
1040        //    min_width.
1041        // 4. apply the maximum width of the table, shrinking columns as
1042        //    necessary, applying minimum column widths as we go
1043
1044        // 1. apply ideal widths, and collect information we'll need later
1045        let mut widths = vec![0.0; columns];
1046        let mut min_widths = vec![0.0; columns];
1047        let mut flexes: Vec<Option<f64>> = vec![None; columns];
1048        let mut table_width = 0.0; // running tally of the sum of widths[x] for all x
1049        let mut unflexed_table_width = 0.0; // sum of the max_intrinsic_widths of any column that has None flex
1050        let mut total_flex = 0.0;
1051        for x in 0..columns {
1052            let column_width = self.column_width_for(app, x);
1053            let column_cells = self.column(app, x);
1054            // apply ideal width (max_intrinsic_width)
1055            let max_intrinsic_width =
1056                column_width.max_intrinsic_width(app, &column_cells, constraints.max_width);
1057            debug_assert!(max_intrinsic_width.is_finite());
1058            debug_assert!(max_intrinsic_width >= 0.0);
1059            widths[x] = max_intrinsic_width;
1060            table_width += max_intrinsic_width;
1061            // collect min width information while we're at it
1062            let min_intrinsic_width =
1063                column_width.min_intrinsic_width(app, &column_cells, constraints.max_width);
1064            debug_assert!(min_intrinsic_width.is_finite());
1065            debug_assert!(min_intrinsic_width >= 0.0);
1066            min_widths[x] = min_intrinsic_width;
1067            debug_assert!(max_intrinsic_width >= min_intrinsic_width);
1068            // collect flex information while we're at it
1069            let flex = column_width.flex(app, &column_cells);
1070            if let Some(flex) = flex {
1071                debug_assert!(flex.is_finite());
1072                debug_assert!(flex > 0.0);
1073                flexes[x] = Some(flex);
1074                total_flex += flex;
1075            } else {
1076                unflexed_table_width += max_intrinsic_width;
1077            }
1078        }
1079        let max_width_constraint = constraints.max_width;
1080        let min_width_constraint = constraints.min_width;
1081
1082        // 2. grow the flex columns so that the table has the max_width (if
1083        //    finite) or the min_width (if not)
1084        if total_flex > 0.0 {
1085            // this can only grow the table, but it _will_ grow the table at
1086            // least as big as the target width.
1087            let target_width = if max_width_constraint.is_finite() {
1088                max_width_constraint
1089            } else {
1090                min_width_constraint
1091            };
1092            if table_width < target_width {
1093                let remaining_width = target_width - unflexed_table_width;
1094                debug_assert!(remaining_width.is_finite());
1095                debug_assert!(remaining_width >= 0.0);
1096                for x in 0..columns {
1097                    if let Some(flex) = flexes[x] {
1098                        let flexed_width = remaining_width * flex / total_flex;
1099                        debug_assert!(flexed_width.is_finite());
1100                        debug_assert!(flexed_width >= 0.0);
1101                        if widths[x] < flexed_width {
1102                            let delta = flexed_width - widths[x];
1103                            table_width += delta;
1104                            widths[x] = flexed_width;
1105                        }
1106                    }
1107                }
1108                debug_assert!(table_width + PRECISION_ERROR_TOLERANCE >= target_width);
1109            }
1110        }
1111        // step 2 and 3 are mutually exclusive
1112        // 3. if there were no flex columns, then grow the table to the
1113        //    min_width.
1114        else if table_width < min_width_constraint {
1115            let delta = (min_width_constraint - table_width) / columns as f64;
1116            for width in &mut widths {
1117                *width += delta;
1118            }
1119            table_width = min_width_constraint;
1120        }
1121
1122        // beyond this point, unflexed_table_width is no longer valid
1123
1124        // 4. apply the maximum width of the table, shrinking columns as
1125        //    necessary, applying minimum column widths as we go
1126        if table_width > max_width_constraint {
1127            let mut deficit = table_width - max_width_constraint;
1128            // Some columns may have low flex but have all the free space.
1129            // (Consider a case with a 1px wide column of flex 1000.0 and
1130            // a 1000px wide column of flex 1.0; the sizes coming from the
1131            // max_intrinsic_widths. If the maximum table width is 2px, then
1132            // just applying the flexes to the deficit would result in a
1133            // table with one column at -998px and one column at 990px,
1134            // which is wildly unhelpful.)
1135            // Similarly, some columns may be flexible, but not actually
1136            // be shrinkable due to a large minimum width. (Consider a
1137            // case with two columns, one is flex and one isn't, both have
1138            // 1000px max_intrinsic_widths, but the flex one has 1000px
1139            // min_intrinsic_width also. The whole deficit will have to come
1140            // from the non-flex column.)
1141            // So what we do is we repeatedly iterate through the flexible
1142            // columns shrinking them proportionally until we have no
1143            // available columns, then do the same to the non-flexible ones.
1144            let mut available_columns = columns;
1145            while deficit > PRECISION_ERROR_TOLERANCE && total_flex > PRECISION_ERROR_TOLERANCE {
1146                let mut new_total_flex = 0.0;
1147                for x in 0..columns {
1148                    if let Some(flex) = flexes[x] {
1149                        let new_width = widths[x] - deficit * flex / total_flex;
1150                        debug_assert!(new_width.is_finite());
1151                        if new_width <= min_widths[x] {
1152                            // shrank to minimum
1153                            deficit -= widths[x] - min_widths[x];
1154                            widths[x] = min_widths[x];
1155                            flexes[x] = None;
1156                            available_columns -= 1;
1157                        } else {
1158                            deficit -= widths[x] - new_width;
1159                            widths[x] = new_width;
1160                            new_total_flex += flex;
1161                        }
1162                        debug_assert!(widths[x] >= 0.0);
1163                    }
1164                }
1165                total_flex = new_total_flex;
1166            }
1167            while deficit > PRECISION_ERROR_TOLERANCE && available_columns > 0 {
1168                // Now we have to take out the remaining space from the
1169                // columns that aren't minimum sized.
1170                // To make this fair, we repeatedly remove equal amounts from
1171                // each column, clamped to the minimum width, until we run out
1172                // of columns that aren't at their min_width.
1173                let delta = deficit / available_columns as f64;
1174                debug_assert!(delta != 0.0);
1175                let mut new_available_columns = 0;
1176                for x in 0..columns {
1177                    let available_delta = widths[x] - min_widths[x];
1178                    if available_delta > 0.0 {
1179                        if available_delta <= delta {
1180                            // shrank to minimum
1181                            deficit -= widths[x] - min_widths[x];
1182                            widths[x] = min_widths[x];
1183                        } else {
1184                            deficit -= delta;
1185                            widths[x] -= delta;
1186                            new_available_columns += 1;
1187                        }
1188                    }
1189                }
1190                available_columns = new_available_columns;
1191            }
1192        }
1193        widths
1194    }
1195
1196    /// Returns the position and dimensions of the box that the given row covers, in this render
1197    /// object's coordinate space (so the left coordinate is always 0.0).
1198    ///
1199    /// The row being queried must exist.
1200    ///
1201    /// This is only valid after layout.
1202    pub fn get_row_box(self: RenderHandle<Self>, app: &App, row: usize) -> Rect {
1203        debug_assert!(row < self.rows(app));
1204        debug_assert!(!self.debug_needs_layout(app));
1205        let row_tops = &self.get(app).row_tops;
1206        Rect::from_ltrb(
1207            0.0,
1208            row_tops[row],
1209            self.size(app).width(),
1210            row_tops[row + 1],
1211        )
1212    }
1213
1214    /// The vertical alignment of `child`: its own, else the table's default.
1215    fn vertical_alignment_of(
1216        self: RenderHandle<Self>,
1217        app: &App,
1218        child: AnyRenderBox,
1219    ) -> TableCellVerticalAlignment {
1220        child
1221            .as_object()
1222            .parent_data_of::<TableCellParentData>(app)
1223            .vertical_alignment
1224            .unwrap_or(self.get(app).default_vertical_alignment)
1225    }
1226
1227    fn set_child_offset(app: &mut App, child: AnyRenderBox, offset: Offset) {
1228        child.parent_data_of_mut::<BoxParentData>(app).offset = offset;
1229    }
1230}
1231
1232impl RenderObject for RenderTable {
1233    crate::render_object_accessors!();
1234
1235    fn did_detach(self: RenderHandle<Self>, app: &mut App) {
1236        let this = self.get_mut(app);
1237        if let Some(painters) = this.row_decoration_painters.take() {
1238            for mut painter in painters.into_iter().flatten() {
1239                painter.dispose();
1240            }
1241            let decorations = this.row_decorations.as_ref().map_or(0, Vec::len);
1242            this.row_decoration_painters = Some((0..decorations).map(|_| None).collect());
1243        }
1244        let children = self.get(app).children.clone();
1245        for child in children.into_iter().flatten() {
1246            child.as_object().detach(app);
1247        }
1248    }
1249
1250    fn visit_children(
1251        self: RenderHandle<Self>,
1252        app: &App,
1253        visitor: &mut dyn FnMut(AnyRenderObject),
1254    ) {
1255        let this = self.get(app);
1256        debug_assert_eq!(this.children.len(), this.rows * this.columns);
1257        for child in this.children.iter().flatten() {
1258            visitor(child.as_object());
1259        }
1260    }
1261
1262    fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1263        let constraints = self.constraints(app);
1264        let rows = self.rows(app);
1265        let columns = self.columns(app);
1266        debug_assert_eq!(self.get(app).children.len(), rows * columns);
1267        if rows * columns == 0 {
1268            // TODO(ianh): if columns is zero, this should be zero width
1269            // TODO(ianh): if columns is not zero, this should be based on the column width
1270            // specifications
1271            self.get_mut(app).table_width = 0.0;
1272            self.set_size(app, constraints.constrain(Size::ZERO));
1273            return;
1274        }
1275        let widths = self.compute_column_widths(app, constraints);
1276        let mut positions = vec![0.0; columns];
1277        let (column_lefts, table_width) = match self.text_direction(app) {
1278            TextDirection::Rtl => {
1279                positions[columns - 1] = 0.0;
1280                for x in (0..columns - 1).rev() {
1281                    positions[x] = positions[x + 1] + widths[x + 1];
1282                }
1283                let table_width = positions[0] + widths[0];
1284                (positions.iter().rev().copied().collect(), table_width)
1285            }
1286            TextDirection::Ltr => {
1287                positions[0] = 0.0;
1288                for x in 1..columns {
1289                    positions[x] = positions[x - 1] + widths[x - 1];
1290                }
1291                let table_width = positions[columns - 1] + widths[columns - 1];
1292                (positions.clone(), table_width)
1293            }
1294        };
1295        let this = self.get_mut(app);
1296        this.column_lefts = Some(column_lefts);
1297        this.table_width = table_width;
1298        this.row_tops.clear();
1299        this.baseline_distance = None;
1300        // then, lay out each row
1301        let mut row_top = 0.0;
1302        for y in 0..rows {
1303            self.get_mut(app).row_tops.push(row_top);
1304            let mut row_height: f64 = 0.0;
1305            let mut have_baseline = false;
1306            let mut before_baseline_distance: f64 = 0.0;
1307            let mut after_baseline_distance: f64 = 0.0;
1308            let mut baselines = vec![0.0; columns];
1309            for x in 0..columns {
1310                let xy = x + y * columns;
1311                let Some(child) = self.get(app).children[xy] else {
1312                    continue;
1313                };
1314                let child_parent_data = child
1315                    .as_object()
1316                    .parent_data_of_mut::<TableCellParentData>(app);
1317                child_parent_data.x = Some(x);
1318                child_parent_data.y = Some(y);
1319                match self.vertical_alignment_of(app, child) {
1320                    TableCellVerticalAlignment::Baseline => {
1321                        let text_baseline = self.text_baseline(app).expect(
1322                            "An explicit textBaseline is required when using baseline alignment.",
1323                        );
1324                        child.layout(app, BoxConstraints::tight_for(Some(widths[x]), None), true);
1325                        let child_baseline =
1326                            child.get_distance_to_baseline(app, text_baseline, true);
1327                        if let Some(child_baseline) = child_baseline {
1328                            before_baseline_distance = before_baseline_distance.max(child_baseline);
1329                            after_baseline_distance = after_baseline_distance
1330                                .max(child.size(app).height() - child_baseline);
1331                            baselines[x] = child_baseline;
1332                            have_baseline = true;
1333                        } else {
1334                            row_height = row_height.max(child.size(app).height());
1335                            RenderTable::set_child_offset(
1336                                app,
1337                                child,
1338                                Offset::new(positions[x], row_top),
1339                            );
1340                        }
1341                    }
1342                    TableCellVerticalAlignment::Top
1343                    | TableCellVerticalAlignment::Middle
1344                    | TableCellVerticalAlignment::Bottom
1345                    | TableCellVerticalAlignment::IntrinsicHeight => {
1346                        child.layout(app, BoxConstraints::tight_for(Some(widths[x]), None), true);
1347                        row_height = row_height.max(child.size(app).height());
1348                    }
1349                    TableCellVerticalAlignment::Fill => {}
1350                }
1351            }
1352            if have_baseline {
1353                if y == 0 {
1354                    self.get_mut(app).baseline_distance = Some(before_baseline_distance);
1355                }
1356                row_height = row_height.max(before_baseline_distance + after_baseline_distance);
1357            }
1358            for x in 0..columns {
1359                let xy = x + y * columns;
1360                let Some(child) = self.get(app).children[xy] else {
1361                    continue;
1362                };
1363                match self.vertical_alignment_of(app, child) {
1364                    TableCellVerticalAlignment::Baseline => {
1365                        RenderTable::set_child_offset(
1366                            app,
1367                            child,
1368                            Offset::new(
1369                                positions[x],
1370                                row_top + before_baseline_distance - baselines[x],
1371                            ),
1372                        );
1373                    }
1374                    TableCellVerticalAlignment::Top => {
1375                        RenderTable::set_child_offset(
1376                            app,
1377                            child,
1378                            Offset::new(positions[x], row_top),
1379                        );
1380                    }
1381                    TableCellVerticalAlignment::Middle => {
1382                        let offset = Offset::new(
1383                            positions[x],
1384                            row_top + (row_height - child.size(app).height()) / 2.0,
1385                        );
1386                        RenderTable::set_child_offset(app, child, offset);
1387                    }
1388                    TableCellVerticalAlignment::Bottom => {
1389                        let offset = Offset::new(
1390                            positions[x],
1391                            row_top + row_height - child.size(app).height(),
1392                        );
1393                        RenderTable::set_child_offset(app, child, offset);
1394                    }
1395                    TableCellVerticalAlignment::Fill
1396                    | TableCellVerticalAlignment::IntrinsicHeight => {
1397                        child.layout(
1398                            app,
1399                            BoxConstraints::tight_for(Some(widths[x]), Some(row_height)),
1400                            false,
1401                        );
1402                        RenderTable::set_child_offset(
1403                            app,
1404                            child,
1405                            Offset::new(positions[x], row_top),
1406                        );
1407                    }
1408                }
1409            }
1410            row_top += row_height;
1411        }
1412        self.get_mut(app).row_tops.push(row_top);
1413        let table_width = self.get(app).table_width;
1414        self.set_size(app, constraints.constrain(Size::new(table_width, row_top)));
1415        debug_assert_eq!(self.get(app).row_tops.len(), rows + 1);
1416    }
1417
1418    fn paint(
1419        self: RenderHandle<Self>,
1420        app: &mut App,
1421        context: &mut PaintingContext,
1422        offset: Offset,
1423    ) {
1424        let rows = self.rows(app);
1425        let columns = self.columns(app);
1426        debug_assert_eq!(self.get(app).children.len(), rows * columns);
1427        if rows * columns == 0 {
1428            if let Some(border) = self.border(app) {
1429                let border_rect =
1430                    Rect::from_ltwh(offset.dx(), offset.dy(), self.get(app).table_width, 0.0);
1431                border.paint(context.canvas(), border_rect, &[], &[]);
1432            }
1433            return;
1434        }
1435        debug_assert_eq!(self.get(app).row_tops.len(), rows + 1);
1436        if self.get(app).row_decorations.is_some() {
1437            let size = self.size(app);
1438            let this = self.get_mut(app);
1439            let decorations = this.row_decorations.as_ref().expect("checked above");
1440            let painters = this
1441                .row_decoration_painters
1442                .as_mut()
1443                .expect("in sync with the decorations");
1444            debug_assert_eq!(decorations.len(), painters.len());
1445            let canvas = context.canvas();
1446            for y in 0..rows {
1447                if decorations.len() <= y {
1448                    break;
1449                }
1450                if let Some(decoration) = &decorations[y] {
1451                    let painter = painters[y].get_or_insert_with(|| {
1452                        decoration.create_box_painter(Some(Rc::new(move |app| {
1453                            self.mark_needs_paint(app)
1454                        })))
1455                    });
1456                    let row_size = Size::new(size.width(), this.row_tops[y + 1] - this.row_tops[y]);
1457                    painter.paint(
1458                        canvas,
1459                        Offset::new(offset.dx(), offset.dy() + this.row_tops[y]),
1460                        &this
1461                            .configuration
1462                            .copy_with(None, None, None, None, Some(row_size), None),
1463                    );
1464                }
1465            }
1466        }
1467        let children = self.get(app).children.clone();
1468        for child in children.into_iter().flatten() {
1469            let child_offset = child.box_parent_data(app).offset;
1470            context.paint_child(app, child.as_object(), child_offset + offset);
1471        }
1472        debug_assert_eq!(rows, self.get(app).row_tops.len() - 1);
1473        debug_assert_eq!(
1474            Some(columns),
1475            self.get(app).column_lefts.as_ref().map(Vec::len)
1476        );
1477        if let Some(border) = self.border(app) {
1478            // The border rect might not fill the entire height of this render object
1479            // if the rows underflow. We always force the columns to fill the width of
1480            // the render object, which means the columns cannot underflow.
1481            let this = self.get(app);
1482            let row_tops = &this.row_tops;
1483            let border_rect = Rect::from_ltwh(
1484                offset.dx(),
1485                offset.dy(),
1486                this.table_width,
1487                row_tops[row_tops.len() - 1],
1488            );
1489            let rows = row_tops[1..row_tops.len() - 1].to_vec();
1490            let columns = this.column_lefts.as_ref().expect("laid out")[1..].to_vec();
1491            border.paint(context.canvas(), border_rect, &rows, &columns);
1492        }
1493    }
1494}
1495
1496impl RenderBox for RenderTable {
1497    crate::render_box_accessors!();
1498
1499    fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1500        if !child.parent_data_is::<TableCellParentData>(app) {
1501            child.set_parent_data(app, TableCellParentData::new());
1502        }
1503    }
1504
1505    fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, _height: f64) -> f64 {
1506        let (rows, columns) = (self.rows(app), self.columns(app));
1507        debug_assert_eq!(self.get(app).children.len(), rows * columns);
1508        if rows * columns == 0 {
1509            return 0.0;
1510        }
1511        let mut total_min_width = 0.0;
1512        for x in 0..columns {
1513            let column_width = self.column_width_for(app, x);
1514            let column_cells = self.column(app, x);
1515            total_min_width += column_width.min_intrinsic_width(app, &column_cells, f64::INFINITY);
1516        }
1517        total_min_width
1518    }
1519
1520    fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, _height: f64) -> f64 {
1521        let (rows, columns) = (self.rows(app), self.columns(app));
1522        debug_assert_eq!(self.get(app).children.len(), rows * columns);
1523        if rows * columns == 0 {
1524            return 0.0;
1525        }
1526        let mut total_max_width = 0.0;
1527        for x in 0..columns {
1528            let column_width = self.column_width_for(app, x);
1529            let column_cells = self.column(app, x);
1530            total_max_width += column_width.max_intrinsic_width(app, &column_cells, f64::INFINITY);
1531        }
1532        total_max_width
1533    }
1534
1535    fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1536        // winner of the 2016 world's most expensive intrinsic dimension function award
1537        // honorable mention, most likely to improve if taught about memoization award
1538        let (rows, columns) = (self.rows(app), self.columns(app));
1539        debug_assert_eq!(self.get(app).children.len(), rows * columns);
1540        if rows * columns == 0 {
1541            return 0.0;
1542        }
1543        let widths =
1544            self.compute_column_widths(app, BoxConstraints::tight_for_finite(width, f64::INFINITY));
1545        let mut row_top = 0.0;
1546        for y in 0..rows {
1547            let mut row_height: f64 = 0.0;
1548            for (x, &width) in widths.iter().enumerate() {
1549                let xy = x + y * columns;
1550                if let Some(child) = self.get(app).children[xy] {
1551                    row_height = row_height.max(child.get_max_intrinsic_height(app, width));
1552                }
1553            }
1554            row_top += row_height;
1555        }
1556        row_top
1557    }
1558
1559    fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1560        self.as_box().get_min_intrinsic_height(app, width)
1561    }
1562
1563    /// Returns the baseline offset of the cell in the first row with the lowest baseline, and
1564    /// uses [`TableCellVerticalAlignment::Baseline`].
1565    fn compute_distance_to_actual_baseline(
1566        self: RenderHandle<Self>,
1567        app: &mut App,
1568        _baseline: TextBaseline,
1569    ) -> Option<f64> {
1570        debug_assert!(!self.debug_needs_layout(app));
1571        self.get(app).baseline_distance
1572    }
1573
1574    fn compute_dry_baseline(
1575        self: RenderHandle<Self>,
1576        app: &mut App,
1577        constraints: BoxConstraints,
1578        baseline: TextBaseline,
1579    ) -> Option<f64> {
1580        let columns = self.columns(app);
1581        if self.rows(app) * columns == 0 {
1582            return None;
1583        }
1584        let widths = self.compute_column_widths(app, constraints);
1585        let mut baseline_offset: Option<f64> = None;
1586        for (col, &width) in widths.iter().enumerate() {
1587            let Some(child) = self.get(app).children[col] else {
1588                continue;
1589            };
1590            let child_constraints = BoxConstraints::tight_for(Some(width), None);
1591            let child_baseline = match self.vertical_alignment_of(app, child) {
1592                TableCellVerticalAlignment::Baseline => {
1593                    child.get_dry_baseline(app, child_constraints, baseline)
1594                }
1595                TableCellVerticalAlignment::Top
1596                | TableCellVerticalAlignment::Middle
1597                | TableCellVerticalAlignment::Bottom
1598                | TableCellVerticalAlignment::Fill
1599                | TableCellVerticalAlignment::IntrinsicHeight => None,
1600            };
1601            if let Some(child_baseline) = child_baseline
1602                && baseline_offset.is_none_or(|offset| offset < child_baseline)
1603            {
1604                baseline_offset = Some(child_baseline);
1605            }
1606        }
1607        baseline_offset
1608    }
1609
1610    fn compute_dry_layout(
1611        self: RenderHandle<Self>,
1612        app: &mut App,
1613        constraints: BoxConstraints,
1614    ) -> Size {
1615        let (rows, columns) = (self.rows(app), self.columns(app));
1616        if rows * columns == 0 {
1617            return constraints.constrain(Size::ZERO);
1618        }
1619        let widths = self.compute_column_widths(app, constraints);
1620        let table_width: f64 = widths.iter().sum();
1621        let mut row_top = 0.0;
1622        for y in 0..rows {
1623            let mut row_height: f64 = 0.0;
1624            for (x, &width) in widths.iter().enumerate() {
1625                let xy = x + y * columns;
1626                let Some(child) = self.get(app).children[xy] else {
1627                    continue;
1628                };
1629                match self.vertical_alignment_of(app, child) {
1630                    TableCellVerticalAlignment::Baseline => {
1631                        self.debug_cannot_compute_dry_layout(DryLayoutFailure::Reason(
1632                            "TableCellVerticalAlignment.baseline requires a full layout for \
1633                             baseline metrics to be available.",
1634                        ));
1635                        return Size::ZERO;
1636                    }
1637                    TableCellVerticalAlignment::Top
1638                    | TableCellVerticalAlignment::Middle
1639                    | TableCellVerticalAlignment::Bottom
1640                    | TableCellVerticalAlignment::IntrinsicHeight => {
1641                        let child_size =
1642                            child.get_dry_layout(app, BoxConstraints::tight_for(Some(width), None));
1643                        row_height = row_height.max(child_size.height());
1644                    }
1645                    TableCellVerticalAlignment::Fill => {}
1646                }
1647            }
1648            row_top += row_height;
1649        }
1650        constraints.constrain(Size::new(table_width, row_top))
1651    }
1652
1653    fn hit_test_children(
1654        self: RenderHandle<Self>,
1655        app: &mut App,
1656        result: &mut BoxHitTestResult<'_>,
1657        position: Offset,
1658    ) -> bool {
1659        debug_assert_eq!(
1660            self.get(app).children.len(),
1661            self.rows(app) * self.columns(app)
1662        );
1663        let children = self.get(app).children.clone();
1664        for child in children.into_iter().rev().flatten() {
1665            let child_offset = child.box_parent_data(app).offset;
1666            let is_hit = result.add_with_paint_offset(
1667                Some(child_offset),
1668                position,
1669                |result, transformed| {
1670                    debug_assert_eq!(transformed, position - child_offset);
1671                    child.hit_test(app, result, transformed)
1672                },
1673            );
1674            if is_hit {
1675                return true;
1676            }
1677        }
1678        false
1679    }
1680}
1681
1682#[cfg(test)]
1683mod tests {
1684    use inset_embedder::valo::Op;
1685    use inset_embedder::{Color, Radius};
1686    use inset_foundation::{AppCell, Handle};
1687    use inset_painting::{AlignmentGeometry, BorderRadius, BorderStyle};
1688
1689    use super::*;
1690    use crate::layer::{ContainerLayer, ErasedLayer, OffsetLayer, PictureLayer};
1691    use crate::pipeline_owner::PipelineOwner;
1692    use crate::proxy_box::{RenderConstrainedBox, RenderRepaintBoundary};
1693    use crate::shifted_box::RenderPositionedBox;
1694
1695    fn sized_box(app: &mut App, width: f64, height: f64) -> AnyRenderBox {
1696        RenderConstrainedBox::new(app, BoxConstraints::tight(Size::new(width, height)), None)
1697            .as_box()
1698    }
1699
1700    fn positioned_box(app: &mut App) -> AnyRenderBox {
1701        RenderPositionedBox::new(app, AlignmentGeometry::CENTER, None, None, None, None).as_box()
1702    }
1703
1704    /// `rendering_tester.dart`'s `layout`: attach `root` under a repaint boundary, lay it out
1705    /// with `constraints`, and paint it. The default constraints are an 800 by 600 screen.
1706    struct Laid {
1707        owner: Handle<PipelineOwner>,
1708        boundary: RenderHandle<RenderRepaintBoundary>,
1709    }
1710
1711    fn layout(app: &mut App, root: AnyRenderBox, constraints: BoxConstraints) -> Laid {
1712        let boundary = RenderRepaintBoundary::new(app, Some(root));
1713        let owner = PipelineOwner::new(app, None, None);
1714        owner.set_root_node(app, Some(boundary.as_object()));
1715        boundary.as_box().layout(app, constraints, false);
1716        let paint_root = OffsetLayer::new(app, Offset::ZERO);
1717        paint_root.as_layer().attach(app, boundary.as_object().id());
1718        boundary
1719            .as_object()
1720            .schedule_initial_paint(app, paint_root.as_container_layer());
1721        pump_frame(app, owner);
1722        Laid { owner, boundary }
1723    }
1724
1725    fn screen() -> BoxConstraints {
1726        BoxConstraints::tight(Size::new(800.0, 600.0))
1727    }
1728
1729    /// `rendering_tester.dart`'s `pumpFrame`: layout, compositing bits and paint.
1730    fn pump_frame(app: &mut App, owner: Handle<PipelineOwner>) {
1731        owner.flush_layout(app);
1732        owner.flush_compositing_bits(app);
1733        owner.flush_paint(app);
1734    }
1735
1736    /// The display list ops the boundary painted, in order.
1737    fn painted(app: &App, laid: &Laid) -> Vec<Op> {
1738        let layer = laid.boundary.as_object().debug_layer(app).expect("painted");
1739        layer
1740            .depth_first_iterate_children(app)
1741            .into_iter()
1742            .filter_map(|child| {
1743                app.handle::<PictureLayer>(child.id())
1744                    .and_then(|picture| picture.picture(app).map(|p| p.ops().to_vec()))
1745            })
1746            .flatten()
1747            .collect()
1748    }
1749
1750    fn paths_painted(app: &App, laid: &Laid) -> usize {
1751        painted(app, laid)
1752            .iter()
1753            .filter(|op| matches!(op, Op::DrawPath { .. }))
1754            .count()
1755    }
1756
1757    /// `table_test.dart`: "Table control test; tight".
1758    #[test]
1759    fn an_empty_table_fills_tight_constraints() {
1760        let cell = AppCell::new();
1761        let mut app = cell.borrow_mut();
1762        let table = RenderTable::new(&mut app, TextDirection::Ltr);
1763        layout(&mut app, table.as_box(), screen());
1764        assert_eq!(table.size(&app), Size::new(800.0, 600.0));
1765    }
1766
1767    /// `table_test.dart`: "Table control test; loose".
1768    #[test]
1769    fn an_empty_table_is_empty_in_loose_constraints() {
1770        let cell = AppCell::new();
1771        let mut app = cell.borrow_mut();
1772        let table = RenderTable::new(&mut app, TextDirection::Ltr);
1773        let center = RenderPositionedBox::new(
1774            &mut app,
1775            AlignmentGeometry::CENTER,
1776            None,
1777            None,
1778            None,
1779            Some(table.as_box()),
1780        );
1781        layout(&mut app, center.as_box(), screen());
1782        assert_eq!(table.size(&app), Size::ZERO);
1783    }
1784
1785    /// `table_test.dart`: "Table control test: constrained flex columns".
1786    #[test]
1787    fn flex_columns_share_the_width() {
1788        let cell = AppCell::new();
1789        let mut app = cell.borrow_mut();
1790        let table = RenderTable::new(&mut app, TextDirection::Ltr);
1791        let children: Vec<AnyRenderBox> = (0..6).map(|_| positioned_box(&mut app)).collect();
1792        table.set_flat_children(&mut app, 6, children.iter().copied().map(Some).collect());
1793        layout(
1794            &mut app,
1795            table.as_box(),
1796            BoxConstraints::tight_for(Some(100.0), None),
1797        );
1798        let expected_width = 100.0 / 6.0;
1799        for child in children {
1800            assert!((child.size(&app).width() - expected_width).abs() < 1e-10);
1801        }
1802    }
1803
1804    /// `table_test.dart`: "Table test: combinations". Boxes have no baseline, so a baseline
1805    /// row top-aligns them.
1806    #[test]
1807    fn intrinsic_columns_fit_their_widest_cell() {
1808        let cell = AppCell::new();
1809        let mut app = cell.borrow_mut();
1810        let table = RenderTable::new(&mut app, TextDirection::Ltr);
1811        table.set_columns(&mut app, 5);
1812        table.set_rows(&mut app, 5);
1813        table.set_default_column_width(&mut app, Rc::new(IntrinsicColumnWidth::new(None)));
1814        table.set_default_vertical_alignment(&mut app, TableCellVerticalAlignment::Baseline);
1815        table.set_text_baseline(&mut app, Some(TextBaseline::Alphabetic));
1816        let center = RenderPositionedBox::new(
1817            &mut app,
1818            AlignmentGeometry::CENTER,
1819            None,
1820            None,
1821            None,
1822            Some(table.as_box()),
1823        );
1824        let laid = layout(&mut app, center.as_box(), screen());
1825        assert_eq!(table.size(&app), Size::ZERO);
1826
1827        let tall = sized_box(&mut app, 100.0, 200.0);
1828        table.set_child(&mut app, 2, 4, Some(tall));
1829        pump_frame(&mut app, laid.owner);
1830        assert_eq!(table.size(&app), Size::new(100.0, 200.0));
1831
1832        let first = sized_box(&mut app, 10.0, 30.0);
1833        let second = sized_box(&mut app, 20.0, 20.0);
1834        let third = sized_box(&mut app, 30.0, 10.0);
1835        table.set_child(&mut app, 0, 0, Some(first));
1836        table.set_child(&mut app, 1, 0, Some(second));
1837        table.set_child(&mut app, 2, 0, Some(third));
1838        pump_frame(&mut app, laid.owner);
1839        assert_eq!(table.size(&app), Size::new(130.0, 230.0));
1840        assert_eq!(
1841            table.get(&app).column_lefts.as_deref(),
1842            Some(&[0.0, 10.0, 30.0, 130.0, 130.0][..])
1843        );
1844        assert_eq!(
1845            table.get(&app).row_tops,
1846            [0.0, 30.0, 30.0, 30.0, 30.0, 230.0]
1847        );
1848        assert_eq!(third.size(&app), Size::new(100.0, 10.0));
1849        assert_eq!(tall.box_parent_data(&app).offset, Offset::new(30.0, 30.0));
1850        let parent_data = third
1851            .as_object()
1852            .parent_data_of::<TableCellParentData>(&app);
1853        assert_eq!((parent_data.x, parent_data.y), (Some(2), Some(0)));
1854    }
1855
1856    /// `table_test.dart`: "Table test: removing cells".
1857    #[test]
1858    fn fewer_rows_drop_the_cells_below() {
1859        let cell = AppCell::new();
1860        let mut app = cell.borrow_mut();
1861        let table = RenderTable::new(&mut app, TextDirection::Ltr);
1862        table.set_columns(&mut app, 5);
1863        table.set_rows(&mut app, 5);
1864        let child = sized_box(&mut app, 10.0, 10.0);
1865        table.set_child(&mut app, 4, 4, Some(child));
1866        layout(&mut app, table.as_box(), screen());
1867        assert!(child.as_object().attached(&app));
1868        table.set_rows(&mut app, 4);
1869        assert!(!child.as_object().attached(&app));
1870    }
1871
1872    /// `table_test.dart`: "Table test: replacing cells". A cell that moves keeps its parent.
1873    #[test]
1874    fn moving_cells_keeps_them_in_the_table() {
1875        let cell = AppCell::new();
1876        let mut app = cell.borrow_mut();
1877        let table = RenderTable::new(&mut app, TextDirection::Ltr);
1878        let moving: Vec<AnyRenderBox> = (0..3).map(|_| positioned_box(&mut app)).collect();
1879        let mut cells = Vec::new();
1880        for child in &moving {
1881            cells.push(Some(*child));
1882            cells.push(Some(positioned_box(&mut app)));
1883        }
1884        table.set_flat_children(&mut app, 3, cells);
1885        assert_eq!(table.rows(&app), 2);
1886        let laid = layout(&mut app, table.as_box(), screen());
1887        for _ in 0..2 {
1888            let mut cells = Vec::new();
1889            for child in &moving {
1890                cells.push(Some(positioned_box(&mut app)));
1891                cells.push(Some(*child));
1892            }
1893            table.set_flat_children(&mut app, 3, cells);
1894            pump_frame(&mut app, laid.owner);
1895        }
1896        assert_eq!(table.columns(&app), 3);
1897        assert_eq!(table.rows(&app), 2);
1898        for child in moving {
1899            assert_eq!(child.as_object().parent(&app), Some(table.as_object()));
1900        }
1901    }
1902
1903    /// `table_test.dart`: "Table border painting": the inner lines are one path per direction,
1904    /// after which the outer border paints its four sides.
1905    #[test]
1906    fn a_border_paints_its_inner_lines_and_four_sides() {
1907        let cell = AppCell::new();
1908        let mut app = cell.borrow_mut();
1909        let table = RenderTable::new(&mut app, TextDirection::Rtl);
1910        table.set_border(
1911            &mut app,
1912            Some(TableBorder::all(
1913                Color::new(0xFF000000),
1914                1.0,
1915                BorderStyle::Solid,
1916                BorderRadius::ZERO,
1917            )),
1918        );
1919        let laid = layout(&mut app, table.as_box(), screen());
1920        for (columns, cells, paths) in [
1921            (1, 0, 4),
1922            (1, 1, 4),
1923            (1, 2, 5),
1924            (2, 2, 5),
1925            (2, 4, 6),
1926            (3, 6, 6),
1927        ] {
1928            let children = (0..cells).map(|_| Some(positioned_box(&mut app))).collect();
1929            table.set_flat_children(&mut app, columns, children);
1930            pump_frame(&mut app, laid.owner);
1931            assert_eq!(
1932                paths_painted(&app, &laid),
1933                paths,
1934                "{columns} columns, {cells} cells"
1935            );
1936        }
1937    }
1938
1939    /// `table_test.dart`: "Table paints a borderRadius": a uniform rounded border is one ring
1940    /// after the two inner lines.
1941    #[test]
1942    fn a_rounded_uniform_border_paints_one_ring() {
1943        let cell = AppCell::new();
1944        let mut app = cell.borrow_mut();
1945        let table = RenderTable::new(&mut app, TextDirection::Ltr);
1946        table.set_border(
1947            &mut app,
1948            Some(TableBorder::all(
1949                Color::new(0xFF000000),
1950                1.0,
1951                BorderStyle::Solid,
1952                BorderRadius::all(Radius::circular(8.0)),
1953            )),
1954        );
1955        let laid = layout(&mut app, table.as_box(), screen());
1956        let children = (0..4).map(|_| Some(positioned_box(&mut app))).collect();
1957        table.set_flat_children(&mut app, 2, children);
1958        pump_frame(&mut app, laid.owner);
1959        assert_eq!(paths_painted(&app, &laid), 3);
1960    }
1961
1962    /// Row decorations paint behind each row, filling it.
1963    #[test]
1964    fn row_decorations_paint_behind_their_rows() {
1965        let cell = AppCell::new();
1966        let mut app = cell.borrow_mut();
1967        let table = RenderTable::new(&mut app, TextDirection::Ltr);
1968        let rows = vec![
1969            vec![sized_box(&mut app, 10.0, 10.0)],
1970            vec![sized_box(&mut app, 10.0, 20.0)],
1971        ];
1972        table.set_children(&mut app, Some(rows));
1973        table.set_row_decorations(
1974            &mut app,
1975            Some(vec![
1976                None,
1977                Some(Box::new(
1978                    inset_painting::BoxDecoration::new().color(Color::new(0xFF0000FF)),
1979                )),
1980            ]),
1981        );
1982        let laid = layout(
1983            &mut app,
1984            table.as_box(),
1985            BoxConstraints::tight_for(Some(100.0), None),
1986        );
1987        let rects: Vec<_> = painted(&app, &laid)
1988            .into_iter()
1989            .filter_map(|op| match op {
1990                Op::DrawRect { rect, .. } => Some(rect),
1991                _ => None,
1992            })
1993            .collect();
1994        assert_eq!(rects.len(), 1, "only the second row is decorated");
1995        assert_eq!(
1996            table.get_row_box(&app, 1),
1997            Rect::from_ltrb(0.0, 10.0, 100.0, 30.0)
1998        );
1999    }
2000
2001    /// `table_test.dart`: "MaxColumnWidth.flex returns the correct result" and
2002    /// "MinColumnWidth.flex returns the correct result".
2003    #[test]
2004    fn max_and_min_widths_take_the_flex_of_either() {
2005        let cell = AppCell::new();
2006        let mut app = cell.borrow_mut();
2007        let fixed: TableColumnWidthRef = Rc::new(FixedColumnWidth::new(100.0));
2008        let flex: TableColumnWidthRef = Rc::new(FlexColumnWidth::new(1.0));
2009        for width in [
2010            Rc::new(MaxColumnWidth::new(Rc::clone(&fixed), Rc::clone(&flex)))
2011                as TableColumnWidthRef,
2012            Rc::new(MaxColumnWidth::new(Rc::clone(&flex), Rc::clone(&fixed))),
2013            Rc::new(MinColumnWidth::new(Rc::clone(&fixed), Rc::clone(&flex))),
2014            Rc::new(MinColumnWidth::new(Rc::clone(&flex), Rc::clone(&fixed))),
2015        ] {
2016            assert_eq!(width.flex(&mut app, &[]), Some(1.0), "{width:?}");
2017        }
2018    }
2019
2020    /// Fixed and fractional columns take their width; a flexible one takes the rest.
2021    #[test]
2022    fn fixed_fraction_and_flex_columns_divide_the_width() {
2023        let cell = AppCell::new();
2024        let mut app = cell.borrow_mut();
2025        let table = RenderTable::new(&mut app, TextDirection::Ltr);
2026        let children: Vec<AnyRenderBox> = (0..3).map(|_| positioned_box(&mut app)).collect();
2027        table.set_flat_children(&mut app, 3, children.iter().copied().map(Some).collect());
2028        let mut widths: HashMap<usize, TableColumnWidthRef> = HashMap::new();
2029        widths.insert(0, Rc::new(FixedColumnWidth::new(50.0)));
2030        widths.insert(1, Rc::new(FractionColumnWidth::new(0.25)));
2031        table.set_column_widths(&mut app, Some(widths));
2032        layout(
2033            &mut app,
2034            table.as_box(),
2035            BoxConstraints::tight_for(Some(400.0), None),
2036        );
2037        let widths: Vec<f64> = children
2038            .iter()
2039            .map(|child| child.size(&app).width())
2040            .collect();
2041        assert_eq!(widths, [50.0, 100.0, 250.0]);
2042    }
2043
2044    /// Too little room shrinks no column below its minimum: the flexible column stops at its
2045    /// fixed lower bound, and the table is cut to its constraints.
2046    #[test]
2047    fn no_column_shrinks_below_its_minimum() {
2048        let cell = AppCell::new();
2049        let mut app = cell.borrow_mut();
2050        let table = RenderTable::new(&mut app, TextDirection::Ltr);
2051        let children: Vec<AnyRenderBox> = (0..2).map(|_| positioned_box(&mut app)).collect();
2052        table.set_flat_children(&mut app, 2, children.iter().copied().map(Some).collect());
2053        let mut widths: HashMap<usize, TableColumnWidthRef> = HashMap::new();
2054        widths.insert(0, Rc::new(FixedColumnWidth::new(150.0)));
2055        widths.insert(
2056            1,
2057            Rc::new(MaxColumnWidth::new(
2058                Rc::new(FixedColumnWidth::new(80.0)),
2059                Rc::new(FlexColumnWidth::new(1.0)),
2060            )),
2061        );
2062        table.set_column_widths(&mut app, Some(widths));
2063        layout(
2064            &mut app,
2065            table.as_box(),
2066            BoxConstraints::tight_for(Some(200.0), None),
2067        );
2068        let widths: Vec<f64> = children
2069            .iter()
2070            .map(|child| child.size(&app).width())
2071            .collect();
2072        assert_eq!(widths, [150.0, 80.0]);
2073        assert_eq!(table.size(&app).width(), 200.0);
2074    }
2075
2076    /// `table_test.dart`: "TableRows with different constraints, but vertically with
2077    /// intrinsicHeight": every cell takes the height of the tallest.
2078    #[test]
2079    fn intrinsic_height_cells_match_the_tallest() {
2080        let cell = AppCell::new();
2081        let mut app = cell.borrow_mut();
2082        let short = RenderConstrainedBox::new(
2083            &mut app,
2084            BoxConstraints::tight_for(Some(100.0), Some(100.0)),
2085            None,
2086        );
2087        let tall = RenderConstrainedBox::new(
2088            &mut app,
2089            BoxConstraints::tight_for(Some(200.0), Some(200.0)),
2090            None,
2091        );
2092        let table = RenderTable::new(&mut app, TextDirection::Rtl);
2093        table.set_default_vertical_alignment(&mut app, TableCellVerticalAlignment::IntrinsicHeight);
2094        table.set_children(&mut app, Some(vec![vec![short.as_box(), tall.as_box()]]));
2095        layout(
2096            &mut app,
2097            table.as_box(),
2098            BoxConstraints::tight(Size::new(300.0, 300.0)),
2099        );
2100        assert_eq!(table.size(&app), Size::new(300.0, 300.0));
2101        assert_eq!(short.size(&app).height(), tall.size(&app).height());
2102    }
2103
2104    /// `table_test.dart`: "Empty table intrinsic dimensions should not crash".
2105    #[test]
2106    fn an_empty_table_has_no_intrinsic_size() {
2107        let cell = AppCell::new();
2108        let mut app = cell.borrow_mut();
2109        let table = RenderTable::new(&mut app, TextDirection::Ltr).as_box();
2110        for extent in [100.0, f64::INFINITY] {
2111            assert_eq!(table.get_min_intrinsic_width(&mut app, extent), 0.0);
2112            assert_eq!(table.get_max_intrinsic_width(&mut app, extent), 0.0);
2113            assert_eq!(table.get_min_intrinsic_height(&mut app, extent), 0.0);
2114            assert_eq!(table.get_max_intrinsic_height(&mut app, extent), 0.0);
2115        }
2116    }
2117
2118    /// The intrinsic widths sum the columns, and the intrinsic height at a width sums the
2119    /// tallest cell of each row; the dry layout matches the layout.
2120    #[test]
2121    fn intrinsics_and_dry_layout_follow_the_cells() {
2122        let cell = AppCell::new();
2123        let mut app = cell.borrow_mut();
2124        let table = RenderTable::new(&mut app, TextDirection::Ltr);
2125        table.set_default_column_width(&mut app, Rc::new(IntrinsicColumnWidth::new(None)));
2126        let rows = vec![
2127            vec![
2128                sized_box(&mut app, 10.0, 30.0),
2129                sized_box(&mut app, 40.0, 5.0),
2130            ],
2131            vec![
2132                sized_box(&mut app, 20.0, 10.0),
2133                sized_box(&mut app, 5.0, 15.0),
2134            ],
2135        ];
2136        table.set_children(&mut app, Some(rows));
2137        let table = table.as_box();
2138        assert_eq!(table.get_min_intrinsic_width(&mut app, f64::INFINITY), 60.0);
2139        assert_eq!(table.get_max_intrinsic_width(&mut app, f64::INFINITY), 60.0);
2140        assert_eq!(table.get_min_intrinsic_height(&mut app, 60.0), 45.0);
2141        assert_eq!(table.get_max_intrinsic_height(&mut app, 60.0), 45.0);
2142        let loose = BoxConstraints::new().max_width(500.0).max_height(500.0);
2143        let dry = table.get_dry_layout(&mut app, loose);
2144        layout(&mut app, table, loose);
2145        assert_eq!(dry, Size::new(60.0, 45.0));
2146        assert_eq!(table.size(&app), dry);
2147    }
2148
2149    /// What the shell does at start-up: the app-wide fonts, here the OS fonts.
2150    fn install_fonts(app: &mut App) {
2151        let binding = inset_painting::PaintingBinding::instance(app);
2152        if !binding.has_fonts(app) {
2153            binding.install_fonts(app, |fonts| {
2154                fonts.add_source(valo_system_fonts::SystemFonts::load());
2155            });
2156        }
2157    }
2158
2159    /// Baseline cells share the row's baseline, and the table's baseline is the first row's.
2160    #[test]
2161    fn baseline_cells_share_the_first_rows_baseline() {
2162        use inset_painting::{TextSpan, TextStyle};
2163
2164        use crate::paragraph::RenderParagraph;
2165
2166        let cell = AppCell::new();
2167        let mut app = cell.borrow_mut();
2168        install_fonts(&mut app);
2169        let paragraph = |app: &mut App, font_size: f64| {
2170            let text = TextSpan::new()
2171                .text("Hg")
2172                .style(TextStyle::new().font_size(font_size))
2173                .into_span();
2174            RenderParagraph::new(app, text, TextDirection::Ltr, None).as_box()
2175        };
2176        let small = paragraph(&mut app, 10.0);
2177        let large = paragraph(&mut app, 30.0);
2178        let table = RenderTable::new(&mut app, TextDirection::Ltr);
2179        table.set_default_vertical_alignment(&mut app, TableCellVerticalAlignment::Baseline);
2180        table.set_text_baseline(&mut app, Some(TextBaseline::Alphabetic));
2181        table.set_children(&mut app, Some(vec![vec![small, large]]));
2182        layout(
2183            &mut app,
2184            table.as_box(),
2185            BoxConstraints::tight_for(Some(400.0), None),
2186        );
2187
2188        let baseline_of = |app: &mut App, child: AnyRenderBox| {
2189            child.box_parent_data(app).offset.dy()
2190                + child
2191                    .get_distance_to_baseline(app, TextBaseline::Alphabetic, true)
2192                    .expect("text has a baseline")
2193        };
2194        let small_baseline = baseline_of(&mut app, small);
2195        let large_baseline = baseline_of(&mut app, large);
2196        assert!((small_baseline - large_baseline).abs() < 1e-9);
2197        assert!(
2198            small.box_parent_data(&app).offset.dy() > 0.0,
2199            "the small text is lowered"
2200        );
2201        assert_eq!(
2202            table
2203                .as_box()
2204                .get_distance_to_baseline(&mut app, TextBaseline::Alphabetic, true),
2205            Some(large_baseline)
2206        );
2207    }
2208}