1use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
16pub enum TrackSize {
17 Px(f32),
19 Fr(f32),
21 Auto,
23 MinContent,
25 MaxContent,
27}
28
29impl Default for TrackSize {
30 fn default() -> Self {
31 Self::Fr(1.0)
32 }
33}
34
35impl TrackSize {
36 #[must_use]
38 pub const fn px(value: f32) -> Self {
39 Self::Px(value)
40 }
41
42 #[must_use]
44 pub const fn fr(value: f32) -> Self {
45 Self::Fr(value)
46 }
47
48 pub const AUTO: Self = Self::Auto;
50}
51
52#[derive(Debug, Clone, Default, Serialize, Deserialize)]
54pub struct GridTemplate {
55 pub columns: Vec<TrackSize>,
57 pub rows: Vec<TrackSize>,
59 pub column_gap: f32,
61 pub row_gap: f32,
63 pub areas: HashMap<String, GridArea>,
65}
66
67impl GridTemplate {
68 #[must_use]
70 pub fn new() -> Self {
71 Self::default()
72 }
73
74 #[must_use]
76 pub fn columns(cols: impl IntoIterator<Item = TrackSize>) -> Self {
77 Self {
78 columns: cols.into_iter().collect(),
79 ..Self::default()
80 }
81 }
82
83 #[must_use]
85 pub fn twelve_column() -> Self {
86 Self {
87 columns: vec![TrackSize::Fr(1.0); 12],
88 column_gap: 16.0,
89 row_gap: 16.0,
90 ..Self::default()
91 }
92 }
93
94 #[must_use]
96 pub fn with_rows(mut self, rows: impl IntoIterator<Item = TrackSize>) -> Self {
97 self.rows = rows.into_iter().collect();
98 self
99 }
100
101 #[must_use]
103 pub const fn with_column_gap(mut self, gap: f32) -> Self {
104 self.column_gap = gap;
105 self
106 }
107
108 #[must_use]
110 pub const fn with_row_gap(mut self, gap: f32) -> Self {
111 self.row_gap = gap;
112 self
113 }
114
115 #[must_use]
117 pub const fn with_gap(mut self, gap: f32) -> Self {
118 self.column_gap = gap;
119 self.row_gap = gap;
120 self
121 }
122
123 #[must_use]
125 pub fn with_area(mut self, name: impl Into<String>, area: GridArea) -> Self {
126 self.areas.insert(name.into(), area);
127 self
128 }
129
130 #[must_use]
132 pub fn column_count(&self) -> usize {
133 self.columns.len()
134 }
135
136 #[must_use]
138 pub fn row_count(&self) -> usize {
139 self.rows.len()
140 }
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
145pub struct GridArea {
146 pub row_start: usize,
148 pub row_end: usize,
150 pub col_start: usize,
152 pub col_end: usize,
154}
155
156impl GridArea {
157 #[must_use]
159 pub const fn new(row_start: usize, col_start: usize, row_end: usize, col_end: usize) -> Self {
160 Self {
161 row_start,
162 row_end,
163 col_start,
164 col_end,
165 }
166 }
167
168 #[must_use]
170 pub const fn cell(row: usize, col: usize) -> Self {
171 Self {
172 row_start: row,
173 row_end: row + 1,
174 col_start: col,
175 col_end: col + 1,
176 }
177 }
178
179 #[must_use]
181 pub const fn row_span(row: usize, col_start: usize, col_end: usize) -> Self {
182 Self {
183 row_start: row,
184 row_end: row + 1,
185 col_start,
186 col_end,
187 }
188 }
189
190 #[must_use]
192 pub const fn col_span(col: usize, row_start: usize, row_end: usize) -> Self {
193 Self {
194 row_start,
195 row_end,
196 col_start: col,
197 col_end: col + 1,
198 }
199 }
200
201 #[must_use]
203 pub const fn row_span_count(&self) -> usize {
204 self.row_end.saturating_sub(self.row_start)
205 }
206
207 #[must_use]
209 pub const fn col_span_count(&self) -> usize {
210 self.col_end.saturating_sub(self.col_start)
211 }
212}
213
214#[derive(Debug, Clone, Default, Serialize, Deserialize)]
216pub struct GridItem {
217 pub column_start: usize,
219 pub column_end: usize,
221 pub row_start: usize,
223 pub row_end: usize,
225 pub area: Option<String>,
227 pub column_span: usize,
229 pub row_span: usize,
231 pub justify_self: Option<GridAlign>,
233 pub align_self: Option<GridAlign>,
235}
236
237impl GridItem {
238 #[must_use]
240 pub fn new() -> Self {
241 Self {
242 column_span: 1,
243 row_span: 1,
244 ..Self::default()
245 }
246 }
247
248 #[must_use]
250 pub const fn column(mut self, col: usize) -> Self {
251 self.column_start = col;
252 self.column_end = col + 1;
253 self
254 }
255
256 #[must_use]
258 pub const fn row(mut self, row: usize) -> Self {
259 self.row_start = row;
260 self.row_end = row + 1;
261 self
262 }
263
264 #[must_use]
266 pub const fn span_columns(mut self, span: usize) -> Self {
267 self.column_span = span;
268 self
269 }
270
271 #[must_use]
273 pub const fn span_rows(mut self, span: usize) -> Self {
274 self.row_span = span;
275 self
276 }
277
278 #[must_use]
280 pub fn in_area(mut self, area: impl Into<String>) -> Self {
281 self.area = Some(area.into());
282 self
283 }
284
285 #[must_use]
287 pub const fn justify_self(mut self, align: GridAlign) -> Self {
288 self.justify_self = Some(align);
289 self
290 }
291
292 #[must_use]
294 pub const fn align_self(mut self, align: GridAlign) -> Self {
295 self.align_self = Some(align);
296 self
297 }
298
299 #[must_use]
301 pub fn effective_column_span(&self) -> usize {
302 if self.column_end > self.column_start {
303 self.column_end - self.column_start
304 } else {
305 self.column_span.max(1)
306 }
307 }
308
309 #[must_use]
311 pub fn effective_row_span(&self) -> usize {
312 if self.row_end > self.row_start {
313 self.row_end - self.row_start
314 } else {
315 self.row_span.max(1)
316 }
317 }
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
322pub enum GridAlign {
323 Start,
325 End,
327 #[default]
329 Center,
330 Stretch,
332}
333
334#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
336pub enum GridAutoFlow {
337 #[default]
339 Row,
340 Column,
342 RowDense,
344 ColumnDense,
346}
347
348#[derive(Debug, Clone, Default)]
350pub struct GridLayout {
351 pub columns: Vec<(f32, f32)>,
353 pub rows: Vec<(f32, f32)>,
355 pub width: f32,
357 pub height: f32,
359}
360
361impl GridLayout {
362 #[must_use]
364 pub fn area_bounds(&self, area: &GridArea) -> Option<(f32, f32, f32, f32)> {
365 if area.col_start >= self.columns.len() || area.row_start >= self.rows.len() {
366 return None;
367 }
368
369 let col_start = area.col_start;
370 let col_end = area.col_end.min(self.columns.len());
371 let row_start = area.row_start;
372 let row_end = area.row_end.min(self.rows.len());
373
374 let x = self.columns.get(col_start).map(|(pos, _)| *pos)?;
375 let y = self.rows.get(row_start).map(|(pos, _)| *pos)?;
376
377 let width: f32 = self.columns[col_start..col_end]
378 .iter()
379 .map(|(_, size)| size)
380 .sum();
381 let height: f32 = self.rows[row_start..row_end]
382 .iter()
383 .map(|(_, size)| size)
384 .sum();
385
386 Some((x, y, width, height))
387 }
388
389 #[must_use]
391 pub fn item_bounds(
392 &self,
393 item: &GridItem,
394 row: usize,
395 col: usize,
396 ) -> Option<(f32, f32, f32, f32)> {
397 let area = GridArea::new(
398 row,
399 col,
400 row + item.effective_row_span(),
401 col + item.effective_column_span(),
402 );
403 self.area_bounds(&area)
404 }
405}
406
407pub fn compute_grid_layout(
409 template: &GridTemplate,
410 available_width: f32,
411 available_height: f32,
412 child_sizes: &[(f32, f32)],
413) -> GridLayout {
414 let columns = compute_track_sizes(
416 &template.columns,
417 available_width,
418 template.column_gap,
419 child_sizes
420 .iter()
421 .map(|(w, _)| *w)
422 .collect::<Vec<_>>()
423 .as_slice(),
424 );
425
426 let row_count = if template.rows.is_empty() {
428 let col_count = template.columns.len().max(1);
430 (child_sizes.len() + col_count - 1) / col_count
431 } else {
432 template.rows.len()
433 };
434
435 let row_templates: Vec<TrackSize> = if template.rows.is_empty() {
437 vec![TrackSize::Auto; row_count]
438 } else {
439 template.rows.clone()
440 };
441
442 let rows = compute_track_sizes(
443 &row_templates,
444 available_height,
445 template.row_gap,
446 child_sizes
447 .iter()
448 .map(|(_, h)| *h)
449 .collect::<Vec<_>>()
450 .as_slice(),
451 );
452
453 let width = columns.last().map(|(pos, size)| pos + size).unwrap_or(0.0);
454 let height = rows.last().map(|(pos, size)| pos + size).unwrap_or(0.0);
455
456 GridLayout {
457 columns,
458 rows,
459 width,
460 height,
461 }
462}
463
464fn compute_track_sizes(
466 tracks: &[TrackSize],
467 available: f32,
468 gap: f32,
469 content_sizes: &[f32],
470) -> Vec<(f32, f32)> {
471 if tracks.is_empty() {
472 return Vec::new();
473 }
474
475 let track_count = tracks.len();
476 let total_gap = gap * (track_count.saturating_sub(1)) as f32;
477 let available_for_tracks = (available - total_gap).max(0.0);
478
479 let mut sizes: Vec<f32> = Vec::with_capacity(track_count);
481 let mut total_fixed = 0.0;
482 let mut total_fr = 0.0;
483
484 for (i, track) in tracks.iter().enumerate() {
485 match track {
486 TrackSize::Px(px) => {
487 sizes.push(*px);
488 total_fixed += px;
489 }
490 TrackSize::Fr(fr) => {
491 sizes.push(0.0); total_fr += fr;
493 }
494 TrackSize::Auto | TrackSize::MinContent | TrackSize::MaxContent => {
495 let content_size = content_sizes.get(i).copied().unwrap_or(0.0);
496 sizes.push(content_size);
497 total_fixed += content_size;
498 }
499 }
500 }
501
502 let remaining = (available_for_tracks - total_fixed).max(0.0);
504 if total_fr > 0.0 {
505 for (i, track) in tracks.iter().enumerate() {
506 if let TrackSize::Fr(fr) = track {
507 sizes[i] = remaining * fr / total_fr;
508 }
509 }
510 }
511
512 let mut result = Vec::with_capacity(track_count);
514 let mut position = 0.0;
515
516 for (i, &size) in sizes.iter().enumerate() {
517 result.push((position, size));
518 position += size;
519 if i < track_count - 1 {
520 position += gap;
521 }
522 }
523
524 result
525}
526
527fn get_explicit_position(item: &GridItem, template: &GridTemplate) -> Option<(usize, usize)> {
529 if item.column_start > 0 && item.row_start > 0 {
530 return Some((item.row_start - 1, item.column_start - 1));
531 }
532 item.area
533 .as_ref()
534 .and_then(|name| template.areas.get(name))
535 .map(|area| (area.row_start, area.col_start))
536}
537
538fn mark_occupied(
540 occupied: &mut Vec<Vec<bool>>,
541 row: usize,
542 col: usize,
543 row_span: usize,
544 col_span: usize,
545 col_count: usize,
546) {
547 ensure_rows(occupied, row + row_span, col_count);
548 for r in row..(row + row_span) {
549 for c in col..(col + col_span).min(col_count) {
550 occupied[r][c] = true;
551 }
552 }
553}
554
555#[must_use]
557pub fn auto_place_items(
558 template: &GridTemplate,
559 items: &[GridItem],
560 flow: GridAutoFlow,
561) -> Vec<(usize, usize)> {
562 let col_count = template.columns.len().max(1);
563 let mut occupied: Vec<Vec<bool>> = Vec::new();
564 let mut placements = Vec::with_capacity(items.len());
565
566 for item in items {
567 if let Some(pos) = get_explicit_position(item, template) {
569 placements.push(pos);
570 continue;
571 }
572
573 let col_span = item.effective_column_span();
575 let row_span = item.effective_row_span();
576
577 let (row, col) = match flow {
578 GridAutoFlow::Row | GridAutoFlow::RowDense => {
579 find_next_position_row(&mut occupied, col_count, col_span, row_span)
580 }
581 GridAutoFlow::Column | GridAutoFlow::ColumnDense => {
582 find_next_position_column(&mut occupied, col_count, col_span, row_span)
583 }
584 };
585
586 mark_occupied(&mut occupied, row, col, row_span, col_span, col_count);
587 placements.push((row, col));
588 }
589
590 placements
591}
592
593fn find_next_position_row(
594 occupied: &mut Vec<Vec<bool>>,
595 col_count: usize,
596 col_span: usize,
597 row_span: usize,
598) -> (usize, usize) {
599 let mut row = 0;
600 loop {
601 ensure_rows(occupied, row + row_span, col_count);
602 for col in 0..=(col_count.saturating_sub(col_span)) {
603 if can_place(occupied, row, col, row_span, col_span) {
604 return (row, col);
605 }
606 }
607 row += 1;
608 }
609}
610
611fn find_next_position_column(
612 occupied: &mut Vec<Vec<bool>>,
613 col_count: usize,
614 col_span: usize,
615 row_span: usize,
616) -> (usize, usize) {
617 for col in 0..=(col_count.saturating_sub(col_span)) {
618 let mut row = 0;
619 loop {
620 ensure_rows(occupied, row + row_span, col_count);
621 if can_place(occupied, row, col, row_span, col_span) {
622 return (row, col);
623 }
624 row += 1;
625 if row > 1000 {
626 break; }
628 }
629 }
630 (0, 0) }
632
633fn ensure_rows(occupied: &mut Vec<Vec<bool>>, min_rows: usize, col_count: usize) {
634 while occupied.len() < min_rows {
635 occupied.push(vec![false; col_count]);
636 }
637}
638
639fn can_place(
640 occupied: &[Vec<bool>],
641 row: usize,
642 col: usize,
643 row_span: usize,
644 col_span: usize,
645) -> bool {
646 for r in row..(row + row_span) {
647 for c in col..(col + col_span) {
648 if let Some(row_data) = occupied.get(r) {
649 if row_data.get(c).copied().unwrap_or(false) {
650 return false;
651 }
652 }
653 }
654 }
655 true
656}
657
658#[cfg(test)]
659mod tests {
660 use super::*;
661
662 #[test]
667 fn test_track_size_default() {
668 assert_eq!(TrackSize::default(), TrackSize::Fr(1.0));
669 }
670
671 #[test]
672 fn test_track_size_px() {
673 let size = TrackSize::px(100.0);
674 assert_eq!(size, TrackSize::Px(100.0));
675 }
676
677 #[test]
678 fn test_track_size_fr() {
679 let size = TrackSize::fr(2.0);
680 assert_eq!(size, TrackSize::Fr(2.0));
681 }
682
683 #[test]
688 fn test_grid_template_new() {
689 let template = GridTemplate::new();
690 assert!(template.columns.is_empty());
691 assert!(template.rows.is_empty());
692 assert_eq!(template.column_gap, 0.0);
693 assert_eq!(template.row_gap, 0.0);
694 }
695
696 #[test]
697 fn test_grid_template_columns() {
698 let template = GridTemplate::columns([TrackSize::px(100.0), TrackSize::fr(1.0)]);
699 assert_eq!(template.columns.len(), 2);
700 assert_eq!(template.columns[0], TrackSize::Px(100.0));
701 assert_eq!(template.columns[1], TrackSize::Fr(1.0));
702 }
703
704 #[test]
705 fn test_grid_template_twelve_column() {
706 let template = GridTemplate::twelve_column();
707 assert_eq!(template.columns.len(), 12);
708 assert_eq!(template.column_gap, 16.0);
709 assert_eq!(template.row_gap, 16.0);
710 }
711
712 #[test]
713 fn test_grid_template_builder() {
714 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(2.0)])
715 .with_rows([TrackSize::px(50.0)])
716 .with_gap(8.0);
717
718 assert_eq!(template.columns.len(), 2);
719 assert_eq!(template.rows.len(), 1);
720 assert_eq!(template.column_gap, 8.0);
721 assert_eq!(template.row_gap, 8.0);
722 }
723
724 #[test]
725 fn test_grid_template_with_area() {
726 let template = GridTemplate::twelve_column()
727 .with_area("header", GridArea::row_span(0, 0, 12))
728 .with_area("sidebar", GridArea::col_span(0, 1, 4));
729
730 assert!(template.areas.contains_key("header"));
731 assert!(template.areas.contains_key("sidebar"));
732 }
733
734 #[test]
739 fn test_grid_area_new() {
740 let area = GridArea::new(1, 2, 3, 4);
741 assert_eq!(area.row_start, 1);
742 assert_eq!(area.col_start, 2);
743 assert_eq!(area.row_end, 3);
744 assert_eq!(area.col_end, 4);
745 }
746
747 #[test]
748 fn test_grid_area_cell() {
749 let area = GridArea::cell(2, 3);
750 assert_eq!(area.row_start, 2);
751 assert_eq!(area.row_end, 3);
752 assert_eq!(area.col_start, 3);
753 assert_eq!(area.col_end, 4);
754 }
755
756 #[test]
757 fn test_grid_area_row_span() {
758 let area = GridArea::row_span(0, 0, 6);
759 assert_eq!(area.row_span_count(), 1);
760 assert_eq!(area.col_span_count(), 6);
761 }
762
763 #[test]
764 fn test_grid_area_col_span() {
765 let area = GridArea::col_span(0, 0, 3);
766 assert_eq!(area.row_span_count(), 3);
767 assert_eq!(area.col_span_count(), 1);
768 }
769
770 #[test]
775 fn test_grid_item_new() {
776 let item = GridItem::new();
777 assert_eq!(item.column_span, 1);
778 assert_eq!(item.row_span, 1);
779 }
780
781 #[test]
782 fn test_grid_item_builder() {
783 let item = GridItem::new()
784 .column(2)
785 .row(1)
786 .span_columns(3)
787 .span_rows(2);
788
789 assert_eq!(item.column_start, 2);
790 assert_eq!(item.row_start, 1);
791 assert_eq!(item.column_span, 3);
792 assert_eq!(item.row_span, 2);
793 }
794
795 #[test]
796 fn test_grid_item_effective_span() {
797 let item1 = GridItem::new().span_columns(2);
798 assert_eq!(item1.effective_column_span(), 2);
799
800 let mut item2 = GridItem::new();
801 item2.column_start = 1;
802 item2.column_end = 4;
803 assert_eq!(item2.effective_column_span(), 3);
804 }
805
806 #[test]
807 fn test_grid_item_in_area() {
808 let item = GridItem::new().in_area("sidebar");
809 assert_eq!(item.area, Some("sidebar".to_string()));
810 }
811
812 #[test]
817 fn test_grid_align_default() {
818 assert_eq!(GridAlign::default(), GridAlign::Center);
819 }
820
821 #[test]
826 fn test_compute_track_sizes_fixed() {
827 let tracks = vec![TrackSize::Px(100.0), TrackSize::Px(200.0)];
828 let result = compute_track_sizes(&tracks, 400.0, 0.0, &[]);
829
830 assert_eq!(result.len(), 2);
831 assert_eq!(result[0], (0.0, 100.0));
832 assert_eq!(result[1], (100.0, 200.0));
833 }
834
835 #[test]
836 fn test_compute_track_sizes_fr() {
837 let tracks = vec![TrackSize::Fr(1.0), TrackSize::Fr(1.0)];
838 let result = compute_track_sizes(&tracks, 200.0, 0.0, &[]);
839
840 assert_eq!(result.len(), 2);
841 assert_eq!(result[0], (0.0, 100.0));
842 assert_eq!(result[1], (100.0, 100.0));
843 }
844
845 #[test]
846 fn test_compute_track_sizes_mixed() {
847 let tracks = vec![TrackSize::Px(50.0), TrackSize::Fr(1.0), TrackSize::Fr(1.0)];
848 let result = compute_track_sizes(&tracks, 250.0, 0.0, &[]);
849
850 assert_eq!(result.len(), 3);
851 assert_eq!(result[0], (0.0, 50.0));
852 assert_eq!(result[1], (50.0, 100.0));
853 assert_eq!(result[2], (150.0, 100.0));
854 }
855
856 #[test]
857 fn test_compute_track_sizes_with_gap() {
858 let tracks = vec![TrackSize::Fr(1.0), TrackSize::Fr(1.0)];
859 let result = compute_track_sizes(&tracks, 210.0, 10.0, &[]);
860
861 assert_eq!(result.len(), 2);
862 assert_eq!(result[0], (0.0, 100.0));
863 assert_eq!(result[1], (110.0, 100.0));
864 }
865
866 #[test]
867 fn test_compute_track_sizes_auto() {
868 let tracks = vec![TrackSize::Auto, TrackSize::Fr(1.0)];
869 let content_sizes = vec![80.0];
870 let result = compute_track_sizes(&tracks, 200.0, 0.0, &content_sizes);
871
872 assert_eq!(result.len(), 2);
873 assert_eq!(result[0], (0.0, 80.0));
874 assert_eq!(result[1], (80.0, 120.0));
875 }
876
877 #[test]
878 fn test_compute_track_sizes_empty() {
879 let result = compute_track_sizes(&[], 200.0, 0.0, &[]);
880 assert!(result.is_empty());
881 }
882
883 #[test]
888 fn test_compute_grid_layout_basic() {
889 let template = GridTemplate::columns([TrackSize::Fr(1.0), TrackSize::Fr(1.0)]);
890 let layout = compute_grid_layout(&template, 200.0, 100.0, &[(50.0, 50.0), (50.0, 50.0)]);
891
892 assert_eq!(layout.columns.len(), 2);
893 assert_eq!(layout.width, 200.0);
894 }
895
896 #[test]
897 fn test_compute_grid_layout_twelve_column() {
898 let template = GridTemplate::twelve_column();
899 let layout = compute_grid_layout(&template, 1200.0, 400.0, &[]);
900
901 assert_eq!(layout.columns.len(), 12);
902 }
906
907 #[test]
908 fn test_grid_layout_area_bounds() {
909 let template = GridTemplate::columns([TrackSize::px(100.0), TrackSize::px(100.0)]);
910 let layout = compute_grid_layout(&template, 200.0, 100.0, &[(50.0, 50.0)]);
911
912 let bounds = layout.area_bounds(&GridArea::cell(0, 0));
913 assert!(bounds.is_some());
914 let (x, y, w, _h) = bounds.unwrap();
915 assert_eq!(x, 0.0);
916 assert_eq!(y, 0.0);
917 assert_eq!(w, 100.0);
918 }
919
920 #[test]
921 fn test_grid_layout_area_bounds_span() {
922 let template = GridTemplate::columns([
923 TrackSize::px(100.0),
924 TrackSize::px(100.0),
925 TrackSize::px(100.0),
926 ]);
927 let layout = compute_grid_layout(&template, 300.0, 100.0, &[(50.0, 50.0)]);
928
929 let bounds = layout.area_bounds(&GridArea::row_span(0, 0, 2));
930 assert!(bounds.is_some());
931 let (x, y, w, _h) = bounds.unwrap();
932 assert_eq!(x, 0.0);
933 assert_eq!(y, 0.0);
934 assert_eq!(w, 200.0);
935 }
936
937 #[test]
942 fn test_auto_place_items_simple() {
943 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
944 let items = vec![GridItem::new(), GridItem::new(), GridItem::new()];
945
946 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
947
948 assert_eq!(placements.len(), 3);
949 assert_eq!(placements[0], (0, 0));
950 assert_eq!(placements[1], (0, 1));
951 assert_eq!(placements[2], (1, 0));
952 }
953
954 #[test]
955 fn test_auto_place_items_with_span() {
956 let template =
957 GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0), TrackSize::fr(1.0)]);
958 let items = vec![
959 GridItem::new().span_columns(2),
960 GridItem::new(),
961 GridItem::new(),
962 ];
963
964 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
965
966 assert_eq!(placements.len(), 3);
967 assert_eq!(placements[0], (0, 0)); assert_eq!(placements[1], (0, 2)); assert_eq!(placements[2], (1, 0)); }
971
972 #[test]
973 fn test_auto_place_items_explicit() {
974 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
975 let items = vec![GridItem::new().column(2).row(2), GridItem::new()];
976
977 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
978
979 assert_eq!(placements[0], (1, 1)); assert_eq!(placements[1], (0, 0)); }
982
983 #[test]
984 fn test_auto_place_items_named_area() {
985 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)])
986 .with_area("main", GridArea::cell(1, 1));
987 let items = vec![GridItem::new().in_area("main"), GridItem::new()];
988
989 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
990
991 assert_eq!(placements[0], (1, 1)); assert_eq!(placements[1], (0, 0)); }
994
995 #[test]
996 fn test_auto_place_items_column_flow() {
997 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
998 let items = vec![GridItem::new(), GridItem::new(), GridItem::new()];
999
1000 let placements = auto_place_items(&template, &items, GridAutoFlow::Column);
1001
1002 assert_eq!(placements.len(), 3);
1003 assert_eq!(placements[0], (0, 0));
1005 assert_eq!(placements[1], (1, 0));
1006 assert_eq!(placements[2], (2, 0));
1007 }
1008
1009 #[test]
1014 fn test_grid_auto_flow_default() {
1015 assert_eq!(GridAutoFlow::default(), GridAutoFlow::Row);
1016 }
1017
1018 #[test]
1019 fn test_grid_auto_flow_all_variants() {
1020 assert_eq!(GridAutoFlow::Row, GridAutoFlow::Row);
1021 assert_eq!(GridAutoFlow::Column, GridAutoFlow::Column);
1022 assert_eq!(GridAutoFlow::RowDense, GridAutoFlow::RowDense);
1023 assert_eq!(GridAutoFlow::ColumnDense, GridAutoFlow::ColumnDense);
1024 }
1025
1026 #[test]
1027 fn test_grid_auto_flow_clone() {
1028 let flow = GridAutoFlow::ColumnDense;
1029 let cloned = flow;
1030 assert_eq!(flow, cloned);
1031 }
1032
1033 #[test]
1034 fn test_grid_auto_flow_debug() {
1035 let flow = GridAutoFlow::RowDense;
1036 let debug = format!("{:?}", flow);
1037 assert!(debug.contains("RowDense"));
1038 }
1039
1040 #[test]
1045 fn test_track_size_auto_const() {
1046 assert_eq!(TrackSize::AUTO, TrackSize::Auto);
1047 }
1048
1049 #[test]
1050 fn test_track_size_min_content() {
1051 let size = TrackSize::MinContent;
1052 assert_eq!(size, TrackSize::MinContent);
1053 }
1054
1055 #[test]
1056 fn test_track_size_max_content() {
1057 let size = TrackSize::MaxContent;
1058 assert_eq!(size, TrackSize::MaxContent);
1059 }
1060
1061 #[test]
1062 fn test_track_size_clone() {
1063 let size = TrackSize::Fr(2.5);
1064 let cloned = size;
1065 assert_eq!(size, cloned);
1066 }
1067
1068 #[test]
1069 fn test_track_size_debug() {
1070 let size = TrackSize::Px(100.0);
1071 let debug = format!("{:?}", size);
1072 assert!(debug.contains("Px"));
1073 assert!(debug.contains("100"));
1074 }
1075
1076 #[test]
1077 fn test_track_size_serialize() {
1078 let size = TrackSize::Fr(1.5);
1079 let json = serde_json::to_string(&size).unwrap();
1080 assert!(json.contains("Fr"));
1081 }
1082
1083 #[test]
1084 fn test_track_size_deserialize() {
1085 let json = r#"{"Px":200.0}"#;
1086 let size: TrackSize = serde_json::from_str(json).unwrap();
1087 assert_eq!(size, TrackSize::Px(200.0));
1088 }
1089
1090 #[test]
1095 fn test_grid_template_with_column_gap() {
1096 let template = GridTemplate::new().with_column_gap(20.0);
1097 assert_eq!(template.column_gap, 20.0);
1098 assert_eq!(template.row_gap, 0.0);
1099 }
1100
1101 #[test]
1102 fn test_grid_template_with_row_gap() {
1103 let template = GridTemplate::new().with_row_gap(15.0);
1104 assert_eq!(template.row_gap, 15.0);
1105 assert_eq!(template.column_gap, 0.0);
1106 }
1107
1108 #[test]
1109 fn test_grid_template_column_count() {
1110 let template =
1111 GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0), TrackSize::fr(1.0)]);
1112 assert_eq!(template.column_count(), 3);
1113 }
1114
1115 #[test]
1116 fn test_grid_template_row_count() {
1117 let template = GridTemplate::new().with_rows([TrackSize::px(50.0), TrackSize::px(100.0)]);
1118 assert_eq!(template.row_count(), 2);
1119 }
1120
1121 #[test]
1122 fn test_grid_template_empty_counts() {
1123 let template = GridTemplate::new();
1124 assert_eq!(template.column_count(), 0);
1125 assert_eq!(template.row_count(), 0);
1126 }
1127
1128 #[test]
1129 fn test_grid_template_default() {
1130 let template = GridTemplate::default();
1131 assert!(template.columns.is_empty());
1132 assert!(template.areas.is_empty());
1133 }
1134
1135 #[test]
1136 fn test_grid_template_clone() {
1137 let template = GridTemplate::twelve_column().with_area("header", GridArea::cell(0, 0));
1138 let cloned = template.clone();
1139 assert_eq!(cloned.columns.len(), 12);
1140 assert!(cloned.areas.contains_key("header"));
1141 }
1142
1143 #[test]
1144 fn test_grid_template_serialize() {
1145 let template = GridTemplate::columns([TrackSize::px(100.0)]);
1146 let json = serde_json::to_string(&template).unwrap();
1147 assert!(json.contains("columns"));
1148 }
1149
1150 #[test]
1155 fn test_grid_area_span_counts_zero() {
1156 let area = GridArea::new(5, 5, 5, 5); assert_eq!(area.row_span_count(), 0);
1158 assert_eq!(area.col_span_count(), 0);
1159 }
1160
1161 #[test]
1162 fn test_grid_area_span_counts_saturating() {
1163 let area = GridArea::new(10, 10, 5, 5); assert_eq!(area.row_span_count(), 0);
1165 assert_eq!(area.col_span_count(), 0);
1166 }
1167
1168 #[test]
1169 fn test_grid_area_clone() {
1170 let area = GridArea::new(1, 2, 3, 4);
1171 let cloned = area;
1172 assert_eq!(area, cloned);
1173 }
1174
1175 #[test]
1176 fn test_grid_area_debug() {
1177 let area = GridArea::cell(0, 0);
1178 let debug = format!("{:?}", area);
1179 assert!(debug.contains("GridArea"));
1180 }
1181
1182 #[test]
1183 fn test_grid_area_serialize() {
1184 let area = GridArea::new(0, 0, 2, 4);
1185 let json = serde_json::to_string(&area).unwrap();
1186 assert!(json.contains("row_start"));
1187 }
1188
1189 #[test]
1190 fn test_grid_area_deserialize() {
1191 let json = r#"{"row_start":1,"row_end":3,"col_start":0,"col_end":2}"#;
1192 let area: GridArea = serde_json::from_str(json).unwrap();
1193 assert_eq!(area.row_start, 1);
1194 assert_eq!(area.col_end, 2);
1195 }
1196
1197 #[test]
1202 fn test_grid_item_default() {
1203 let item = GridItem::default();
1204 assert_eq!(item.column_start, 0);
1205 assert_eq!(item.row_start, 0);
1206 assert!(item.area.is_none());
1207 }
1208
1209 #[test]
1210 fn test_grid_item_justify_self() {
1211 let item = GridItem::new().justify_self(GridAlign::End);
1212 assert_eq!(item.justify_self, Some(GridAlign::End));
1213 }
1214
1215 #[test]
1216 fn test_grid_item_align_self() {
1217 let item = GridItem::new().align_self(GridAlign::Stretch);
1218 assert_eq!(item.align_self, Some(GridAlign::Stretch));
1219 }
1220
1221 #[test]
1222 fn test_grid_item_effective_row_span() {
1223 let item = GridItem::new().span_rows(3);
1224 assert_eq!(item.effective_row_span(), 3);
1225
1226 let mut item2 = GridItem::new();
1227 item2.row_start = 1;
1228 item2.row_end = 5;
1229 assert_eq!(item2.effective_row_span(), 4);
1230 }
1231
1232 #[test]
1233 fn test_grid_item_effective_span_minimum() {
1234 let mut item = GridItem::new();
1235 item.column_span = 0; assert_eq!(item.effective_column_span(), 1); item.row_span = 0;
1239 assert_eq!(item.effective_row_span(), 1); }
1241
1242 #[test]
1243 fn test_grid_item_clone() {
1244 let item = GridItem::new().column(2).row(3).span_columns(2);
1245 let cloned = item.clone();
1246 assert_eq!(cloned.column_start, 2);
1247 assert_eq!(cloned.row_start, 3);
1248 }
1249
1250 #[test]
1251 fn test_grid_item_serialize() {
1252 let item = GridItem::new().column(1).row(1);
1253 let json = serde_json::to_string(&item).unwrap();
1254 assert!(json.contains("column_start"));
1255 }
1256
1257 #[test]
1262 fn test_grid_align_all_variants() {
1263 assert_eq!(GridAlign::Start, GridAlign::Start);
1264 assert_eq!(GridAlign::End, GridAlign::End);
1265 assert_eq!(GridAlign::Center, GridAlign::Center);
1266 assert_eq!(GridAlign::Stretch, GridAlign::Stretch);
1267 }
1268
1269 #[test]
1270 fn test_grid_align_clone() {
1271 let align = GridAlign::Stretch;
1272 let cloned = align;
1273 assert_eq!(align, cloned);
1274 }
1275
1276 #[test]
1277 fn test_grid_align_debug() {
1278 let align = GridAlign::Start;
1279 let debug = format!("{:?}", align);
1280 assert!(debug.contains("Start"));
1281 }
1282
1283 #[test]
1284 fn test_grid_align_serialize() {
1285 let align = GridAlign::End;
1286 let json = serde_json::to_string(&align).unwrap();
1287 assert!(json.contains("End"));
1288 }
1289
1290 #[test]
1295 fn test_grid_layout_default() {
1296 let layout = GridLayout::default();
1297 assert!(layout.columns.is_empty());
1298 assert!(layout.rows.is_empty());
1299 assert_eq!(layout.width, 0.0);
1300 assert_eq!(layout.height, 0.0);
1301 }
1302
1303 #[test]
1304 fn test_grid_layout_area_bounds_out_of_range() {
1305 let layout = GridLayout {
1306 columns: vec![(0.0, 100.0)],
1307 rows: vec![(0.0, 50.0)],
1308 width: 100.0,
1309 height: 50.0,
1310 };
1311
1312 let bounds = layout.area_bounds(&GridArea::cell(10, 10));
1314 assert!(bounds.is_none());
1315 }
1316
1317 #[test]
1318 fn test_grid_layout_area_bounds_partial_out_of_range() {
1319 let layout = GridLayout {
1320 columns: vec![(0.0, 100.0), (100.0, 100.0)],
1321 rows: vec![(0.0, 50.0)],
1322 width: 200.0,
1323 height: 50.0,
1324 };
1325
1326 let bounds = layout.area_bounds(&GridArea::new(0, 0, 5, 5));
1328 assert!(bounds.is_some());
1329 let (_, _, w, _) = bounds.unwrap();
1330 assert_eq!(w, 200.0); }
1332
1333 #[test]
1334 fn test_grid_layout_item_bounds() {
1335 let layout = GridLayout {
1336 columns: vec![(0.0, 100.0), (100.0, 100.0)],
1337 rows: vec![(0.0, 50.0), (50.0, 50.0)],
1338 width: 200.0,
1339 height: 100.0,
1340 };
1341
1342 let item = GridItem::new().span_columns(2);
1343 let bounds = layout.item_bounds(&item, 0, 0);
1344 assert!(bounds.is_some());
1345 let (x, y, w, h) = bounds.unwrap();
1346 assert_eq!(x, 0.0);
1347 assert_eq!(y, 0.0);
1348 assert_eq!(w, 200.0);
1349 assert_eq!(h, 50.0);
1350 }
1351
1352 #[test]
1353 fn test_grid_layout_clone() {
1354 let layout = GridLayout {
1355 columns: vec![(0.0, 100.0)],
1356 rows: vec![(0.0, 50.0)],
1357 width: 100.0,
1358 height: 50.0,
1359 };
1360 let cloned = layout.clone();
1361 assert_eq!(cloned.width, 100.0);
1362 }
1363
1364 #[test]
1365 fn test_grid_layout_debug() {
1366 let layout = GridLayout::default();
1367 let debug = format!("{:?}", layout);
1368 assert!(debug.contains("GridLayout"));
1369 }
1370
1371 #[test]
1376 fn test_compute_track_sizes_all_fr() {
1377 let tracks = vec![TrackSize::Fr(1.0), TrackSize::Fr(2.0), TrackSize::Fr(1.0)];
1378 let result = compute_track_sizes(&tracks, 400.0, 0.0, &[]);
1379
1380 assert_eq!(result.len(), 3);
1381 assert_eq!(result[0].1, 100.0); assert_eq!(result[1].1, 200.0); assert_eq!(result[2].1, 100.0); }
1385
1386 #[test]
1387 fn test_compute_track_sizes_zero_fr() {
1388 let tracks = vec![TrackSize::Fr(0.0), TrackSize::Fr(1.0)];
1389 let result = compute_track_sizes(&tracks, 200.0, 0.0, &[]);
1390
1391 assert_eq!(result.len(), 2);
1392 assert_eq!(result[0].1, 0.0);
1393 assert_eq!(result[1].1, 200.0);
1394 }
1395
1396 #[test]
1397 fn test_compute_track_sizes_insufficient_space() {
1398 let tracks = vec![TrackSize::Px(300.0), TrackSize::Fr(1.0)];
1399 let result = compute_track_sizes(&tracks, 200.0, 0.0, &[]);
1400
1401 assert_eq!(result[0].1, 300.0);
1403 assert_eq!(result[1].1, 0.0);
1404 }
1405
1406 #[test]
1407 fn test_compute_track_sizes_large_gap() {
1408 let tracks = vec![TrackSize::Fr(1.0), TrackSize::Fr(1.0)];
1409 let result = compute_track_sizes(&tracks, 100.0, 200.0, &[]);
1410
1411 assert!(result[0].1 <= 0.0);
1413 }
1414
1415 #[test]
1416 fn test_compute_track_sizes_min_content() {
1417 let tracks = vec![TrackSize::MinContent, TrackSize::Fr(1.0)];
1418 let content = vec![60.0, 0.0];
1419 let result = compute_track_sizes(&tracks, 200.0, 0.0, &content);
1420
1421 assert_eq!(result[0].1, 60.0);
1422 assert_eq!(result[1].1, 140.0);
1423 }
1424
1425 #[test]
1426 fn test_compute_track_sizes_max_content() {
1427 let tracks = vec![TrackSize::MaxContent, TrackSize::Fr(1.0)];
1428 let content = vec![80.0];
1429 let result = compute_track_sizes(&tracks, 200.0, 0.0, &content);
1430
1431 assert_eq!(result[0].1, 80.0);
1432 assert_eq!(result[1].1, 120.0);
1433 }
1434
1435 #[test]
1440 fn test_compute_grid_layout_empty_template() {
1441 let template = GridTemplate::new();
1442 let layout = compute_grid_layout(&template, 200.0, 100.0, &[]);
1443
1444 assert!(layout.columns.is_empty());
1445 assert_eq!(layout.width, 0.0);
1446 }
1447
1448 #[test]
1449 fn test_compute_grid_layout_auto_rows() {
1450 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
1451 let children = vec![(50.0, 30.0), (50.0, 30.0), (50.0, 30.0), (50.0, 30.0)];
1452 let layout = compute_grid_layout(&template, 200.0, 200.0, &children);
1453
1454 assert_eq!(layout.rows.len(), 2);
1456 }
1457
1458 #[test]
1459 fn test_compute_grid_layout_with_gaps() {
1460 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)])
1461 .with_rows([TrackSize::px(50.0)])
1462 .with_gap(10.0);
1463 let layout = compute_grid_layout(&template, 210.0, 50.0, &[]);
1464
1465 let col1_end = layout.columns[0].0 + layout.columns[0].1;
1467 let col2_start = layout.columns[1].0;
1468 assert!((col2_start - col1_end - 10.0).abs() < 0.01);
1469 }
1470
1471 #[test]
1476 fn test_auto_place_items_empty() {
1477 let template = GridTemplate::columns([TrackSize::fr(1.0)]);
1478 let placements = auto_place_items(&template, &[], GridAutoFlow::Row);
1479 assert!(placements.is_empty());
1480 }
1481
1482 #[test]
1483 fn test_auto_place_items_single_column() {
1484 let template = GridTemplate::columns([TrackSize::fr(1.0)]);
1485 let items = vec![GridItem::new(), GridItem::new(), GridItem::new()];
1486 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
1487
1488 assert_eq!(placements[0], (0, 0));
1489 assert_eq!(placements[1], (1, 0));
1490 assert_eq!(placements[2], (2, 0));
1491 }
1492
1493 #[test]
1494 fn test_auto_place_items_span_exceeds_grid() {
1495 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
1496 let items = vec![GridItem::new().span_columns(5)]; let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
1498
1499 assert_eq!(placements.len(), 1);
1501 }
1502
1503 #[test]
1504 fn test_auto_place_items_row_span() {
1505 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
1506 let items = vec![
1507 GridItem::new().span_rows(2),
1508 GridItem::new(),
1509 GridItem::new(),
1510 ];
1511 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
1512
1513 assert_eq!(placements[0], (0, 0));
1515 assert_eq!(placements[1], (0, 1));
1516 }
1517
1518 #[test]
1519 fn test_auto_place_items_missing_area() {
1520 let template = GridTemplate::columns([TrackSize::fr(1.0)]);
1521 let items = vec![GridItem::new().in_area("nonexistent")];
1522 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
1523
1524 assert_eq!(placements[0], (0, 0));
1526 }
1527
1528 #[test]
1529 fn test_auto_place_items_column_dense() {
1530 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
1531 let items = vec![GridItem::new(), GridItem::new()];
1532 let placements = auto_place_items(&template, &items, GridAutoFlow::ColumnDense);
1533
1534 assert_eq!(placements.len(), 2);
1536 }
1537}