1use 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#[derive(Debug, Default)]
25pub struct TableCellParentData {
26 box_parent_data: BoxParentData,
27
28 pub vertical_alignment: Option<TableCellVerticalAlignment>,
33
34 pub x: Option<usize>,
36
37 pub y: Option<usize>,
39}
40
41impl TableCellParentData {
42 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
79pub type TableColumnWidthRef = Rc<dyn TableColumnWidth>;
82
83pub trait TableColumnWidth: fmt::Debug {
92 fn min_intrinsic_width(
101 &self,
102 app: &mut App,
103 cells: &[AnyRenderBox],
104 container_width: f64,
105 ) -> f64;
106
107 fn max_intrinsic_width(
119 &self,
120 app: &mut App,
121 cells: &[AnyRenderBox],
122 container_width: f64,
123 ) -> f64;
124
125 fn flex(&self, app: &mut App, cells: &[AnyRenderBox]) -> Option<f64> {
133 let _ = (app, cells);
134 None
135 }
136
137 fn as_any(&self) -> &dyn Any;
139}
140
141pub struct IntrinsicColumnWidth {
148 flex: Option<f64>,
149}
150
151impl IntrinsicColumnWidth {
152 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
209pub struct FixedColumnWidth {
213 pub value: f64,
215}
216
217impl FixedColumnWidth {
218 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
254pub struct FractionColumnWidth {
258 pub value: f64,
260}
261
262impl FractionColumnWidth {
263 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
305pub struct FlexColumnWidth {
313 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 pub const fn new(value: f64) -> FlexColumnWidth {
326 FlexColumnWidth { value }
327 }
328
329 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#[derive(Debug)]
382pub struct MaxColumnWidth {
383 pub a: TableColumnWidthRef,
385 pub b: TableColumnWidthRef,
387}
388
389impl MaxColumnWidth {
390 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#[derive(Debug)]
446pub struct MinColumnWidth {
447 pub a: TableColumnWidthRef,
449 pub b: TableColumnWidthRef,
451}
452
453impl MinColumnWidth {
454 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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
503pub enum TableCellVerticalAlignment {
504 #[default]
506 Top,
507
508 Middle,
510
511 Bottom,
513
514 Baseline,
522
523 Fill,
526
527 IntrinsicHeight,
530}
531
532pub struct RenderTable {
534 render_object: RenderObjectData,
535 render_box: RenderBoxData,
536 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: 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 row_tops: Vec<f64>,
554 column_lefts: Option<Vec<f64>>,
555 table_width: f64,
556 baseline_distance: Option<f64>,
557}
558
559impl RenderTable {
560 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 pub fn columns(self: RenderHandle<Self>, app: &App) -> usize {
605 self.get(app).columns
606 }
607
608 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 pub fn rows(self: RenderHandle<Self>, app: &App) -> usize {
643 self.get(app).rows
644 }
645
646 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 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 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 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 pub fn default_column_width(self: RenderHandle<Self>, app: &App) -> TableColumnWidthRef {
722 Rc::clone(&self.get(app).default_column_width)
723 }
724
725 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 pub fn text_direction(self: RenderHandle<Self>, app: &App) -> TextDirection {
740 self.get(app).text_direction
741 }
742
743 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 pub fn border(self: RenderHandle<Self>, app: &App) -> Option<TableBorder> {
754 self.get(app).border
755 }
756
757 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 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 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 pub fn configuration(self: RenderHandle<Self>, app: &App) -> &ImageConfiguration {
796 &self.get(app).configuration
797 }
798
799 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 pub fn default_vertical_alignment(
810 self: RenderHandle<Self>,
811 app: &App,
812 ) -> TableCellVerticalAlignment {
813 self.get(app).default_vertical_alignment
814 }
815
816 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 pub fn text_baseline(self: RenderHandle<Self>, app: &App) -> Option<TextBaseline> {
832 self.get(app).text_baseline
833 }
834
835 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 pub fn set_flat_children(
852 self: RenderHandle<Self>,
853 app: &mut App,
854 columns: usize,
855 cells: Vec<Option<AnyRenderBox>>,
856 ) {
857 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 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 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 for child in lost_children {
909 self.drop_child(app, child.as_object());
910 }
911 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 pub fn set_children(
922 self: RenderHandle<Self>,
923 app: &mut App,
924 cells: Option<Vec<Vec<AnyRenderBox>>>,
925 ) {
926 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 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 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 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 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 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 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; let mut unflexed_table_width = 0.0; 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 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 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 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 if total_flex > 0.0 {
1085 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 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 if table_width > max_width_constraint {
1127 let mut deficit = table_width - max_width_constraint;
1128 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 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 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 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 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 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 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 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 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 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 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 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 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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 #[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}