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
527#[must_use]
529pub fn auto_place_items(
530 template: &GridTemplate,
531 items: &[GridItem],
532 flow: GridAutoFlow,
533) -> Vec<(usize, usize)> {
534 let col_count = template.columns.len().max(1);
535 let mut occupied: Vec<Vec<bool>> = Vec::new();
536 let mut placements = Vec::with_capacity(items.len());
537
538 for item in items {
539 if item.column_start > 0 && item.row_start > 0 {
541 placements.push((item.row_start - 1, item.column_start - 1));
542 continue;
543 }
544
545 if let Some(area_name) = &item.area {
547 if let Some(area) = template.areas.get(area_name) {
548 placements.push((area.row_start, area.col_start));
549 continue;
550 }
551 }
552
553 let col_span = item.effective_column_span();
555 let row_span = item.effective_row_span();
556
557 let (row, col) = match flow {
558 GridAutoFlow::Row | GridAutoFlow::RowDense => {
559 find_next_position_row(&mut occupied, col_count, col_span, row_span)
560 }
561 GridAutoFlow::Column | GridAutoFlow::ColumnDense => {
562 find_next_position_column(&mut occupied, col_count, col_span, row_span)
563 }
564 };
565
566 ensure_rows(&mut occupied, row + row_span, col_count);
568 for r in row..(row + row_span) {
569 for c in col..(col + col_span) {
570 if c < col_count {
571 occupied[r][c] = true;
572 }
573 }
574 }
575
576 placements.push((row, col));
577 }
578
579 placements
580}
581
582fn find_next_position_row(
583 occupied: &mut Vec<Vec<bool>>,
584 col_count: usize,
585 col_span: usize,
586 row_span: usize,
587) -> (usize, usize) {
588 let mut row = 0;
589 loop {
590 ensure_rows(occupied, row + row_span, col_count);
591 for col in 0..=(col_count.saturating_sub(col_span)) {
592 if can_place(occupied, row, col, row_span, col_span) {
593 return (row, col);
594 }
595 }
596 row += 1;
597 }
598}
599
600fn find_next_position_column(
601 occupied: &mut Vec<Vec<bool>>,
602 col_count: usize,
603 col_span: usize,
604 row_span: usize,
605) -> (usize, usize) {
606 for col in 0..=(col_count.saturating_sub(col_span)) {
607 let mut row = 0;
608 loop {
609 ensure_rows(occupied, row + row_span, col_count);
610 if can_place(occupied, row, col, row_span, col_span) {
611 return (row, col);
612 }
613 row += 1;
614 if row > 1000 {
615 break; }
617 }
618 }
619 (0, 0) }
621
622fn ensure_rows(occupied: &mut Vec<Vec<bool>>, min_rows: usize, col_count: usize) {
623 while occupied.len() < min_rows {
624 occupied.push(vec![false; col_count]);
625 }
626}
627
628fn can_place(
629 occupied: &[Vec<bool>],
630 row: usize,
631 col: usize,
632 row_span: usize,
633 col_span: usize,
634) -> bool {
635 for r in row..(row + row_span) {
636 for c in col..(col + col_span) {
637 if let Some(row_data) = occupied.get(r) {
638 if row_data.get(c).copied().unwrap_or(false) {
639 return false;
640 }
641 }
642 }
643 }
644 true
645}
646
647#[cfg(test)]
648mod tests {
649 use super::*;
650
651 #[test]
656 fn test_track_size_default() {
657 assert_eq!(TrackSize::default(), TrackSize::Fr(1.0));
658 }
659
660 #[test]
661 fn test_track_size_px() {
662 let size = TrackSize::px(100.0);
663 assert_eq!(size, TrackSize::Px(100.0));
664 }
665
666 #[test]
667 fn test_track_size_fr() {
668 let size = TrackSize::fr(2.0);
669 assert_eq!(size, TrackSize::Fr(2.0));
670 }
671
672 #[test]
677 fn test_grid_template_new() {
678 let template = GridTemplate::new();
679 assert!(template.columns.is_empty());
680 assert!(template.rows.is_empty());
681 assert_eq!(template.column_gap, 0.0);
682 assert_eq!(template.row_gap, 0.0);
683 }
684
685 #[test]
686 fn test_grid_template_columns() {
687 let template = GridTemplate::columns([TrackSize::px(100.0), TrackSize::fr(1.0)]);
688 assert_eq!(template.columns.len(), 2);
689 assert_eq!(template.columns[0], TrackSize::Px(100.0));
690 assert_eq!(template.columns[1], TrackSize::Fr(1.0));
691 }
692
693 #[test]
694 fn test_grid_template_twelve_column() {
695 let template = GridTemplate::twelve_column();
696 assert_eq!(template.columns.len(), 12);
697 assert_eq!(template.column_gap, 16.0);
698 assert_eq!(template.row_gap, 16.0);
699 }
700
701 #[test]
702 fn test_grid_template_builder() {
703 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(2.0)])
704 .with_rows([TrackSize::px(50.0)])
705 .with_gap(8.0);
706
707 assert_eq!(template.columns.len(), 2);
708 assert_eq!(template.rows.len(), 1);
709 assert_eq!(template.column_gap, 8.0);
710 assert_eq!(template.row_gap, 8.0);
711 }
712
713 #[test]
714 fn test_grid_template_with_area() {
715 let template = GridTemplate::twelve_column()
716 .with_area("header", GridArea::row_span(0, 0, 12))
717 .with_area("sidebar", GridArea::col_span(0, 1, 4));
718
719 assert!(template.areas.contains_key("header"));
720 assert!(template.areas.contains_key("sidebar"));
721 }
722
723 #[test]
728 fn test_grid_area_new() {
729 let area = GridArea::new(1, 2, 3, 4);
730 assert_eq!(area.row_start, 1);
731 assert_eq!(area.col_start, 2);
732 assert_eq!(area.row_end, 3);
733 assert_eq!(area.col_end, 4);
734 }
735
736 #[test]
737 fn test_grid_area_cell() {
738 let area = GridArea::cell(2, 3);
739 assert_eq!(area.row_start, 2);
740 assert_eq!(area.row_end, 3);
741 assert_eq!(area.col_start, 3);
742 assert_eq!(area.col_end, 4);
743 }
744
745 #[test]
746 fn test_grid_area_row_span() {
747 let area = GridArea::row_span(0, 0, 6);
748 assert_eq!(area.row_span_count(), 1);
749 assert_eq!(area.col_span_count(), 6);
750 }
751
752 #[test]
753 fn test_grid_area_col_span() {
754 let area = GridArea::col_span(0, 0, 3);
755 assert_eq!(area.row_span_count(), 3);
756 assert_eq!(area.col_span_count(), 1);
757 }
758
759 #[test]
764 fn test_grid_item_new() {
765 let item = GridItem::new();
766 assert_eq!(item.column_span, 1);
767 assert_eq!(item.row_span, 1);
768 }
769
770 #[test]
771 fn test_grid_item_builder() {
772 let item = GridItem::new()
773 .column(2)
774 .row(1)
775 .span_columns(3)
776 .span_rows(2);
777
778 assert_eq!(item.column_start, 2);
779 assert_eq!(item.row_start, 1);
780 assert_eq!(item.column_span, 3);
781 assert_eq!(item.row_span, 2);
782 }
783
784 #[test]
785 fn test_grid_item_effective_span() {
786 let item1 = GridItem::new().span_columns(2);
787 assert_eq!(item1.effective_column_span(), 2);
788
789 let mut item2 = GridItem::new();
790 item2.column_start = 1;
791 item2.column_end = 4;
792 assert_eq!(item2.effective_column_span(), 3);
793 }
794
795 #[test]
796 fn test_grid_item_in_area() {
797 let item = GridItem::new().in_area("sidebar");
798 assert_eq!(item.area, Some("sidebar".to_string()));
799 }
800
801 #[test]
806 fn test_grid_align_default() {
807 assert_eq!(GridAlign::default(), GridAlign::Center);
808 }
809
810 #[test]
815 fn test_compute_track_sizes_fixed() {
816 let tracks = vec![TrackSize::Px(100.0), TrackSize::Px(200.0)];
817 let result = compute_track_sizes(&tracks, 400.0, 0.0, &[]);
818
819 assert_eq!(result.len(), 2);
820 assert_eq!(result[0], (0.0, 100.0));
821 assert_eq!(result[1], (100.0, 200.0));
822 }
823
824 #[test]
825 fn test_compute_track_sizes_fr() {
826 let tracks = vec![TrackSize::Fr(1.0), TrackSize::Fr(1.0)];
827 let result = compute_track_sizes(&tracks, 200.0, 0.0, &[]);
828
829 assert_eq!(result.len(), 2);
830 assert_eq!(result[0], (0.0, 100.0));
831 assert_eq!(result[1], (100.0, 100.0));
832 }
833
834 #[test]
835 fn test_compute_track_sizes_mixed() {
836 let tracks = vec![TrackSize::Px(50.0), TrackSize::Fr(1.0), TrackSize::Fr(1.0)];
837 let result = compute_track_sizes(&tracks, 250.0, 0.0, &[]);
838
839 assert_eq!(result.len(), 3);
840 assert_eq!(result[0], (0.0, 50.0));
841 assert_eq!(result[1], (50.0, 100.0));
842 assert_eq!(result[2], (150.0, 100.0));
843 }
844
845 #[test]
846 fn test_compute_track_sizes_with_gap() {
847 let tracks = vec![TrackSize::Fr(1.0), TrackSize::Fr(1.0)];
848 let result = compute_track_sizes(&tracks, 210.0, 10.0, &[]);
849
850 assert_eq!(result.len(), 2);
851 assert_eq!(result[0], (0.0, 100.0));
852 assert_eq!(result[1], (110.0, 100.0));
853 }
854
855 #[test]
856 fn test_compute_track_sizes_auto() {
857 let tracks = vec![TrackSize::Auto, TrackSize::Fr(1.0)];
858 let content_sizes = vec![80.0];
859 let result = compute_track_sizes(&tracks, 200.0, 0.0, &content_sizes);
860
861 assert_eq!(result.len(), 2);
862 assert_eq!(result[0], (0.0, 80.0));
863 assert_eq!(result[1], (80.0, 120.0));
864 }
865
866 #[test]
867 fn test_compute_track_sizes_empty() {
868 let result = compute_track_sizes(&[], 200.0, 0.0, &[]);
869 assert!(result.is_empty());
870 }
871
872 #[test]
877 fn test_compute_grid_layout_basic() {
878 let template = GridTemplate::columns([TrackSize::Fr(1.0), TrackSize::Fr(1.0)]);
879 let layout = compute_grid_layout(&template, 200.0, 100.0, &[(50.0, 50.0), (50.0, 50.0)]);
880
881 assert_eq!(layout.columns.len(), 2);
882 assert_eq!(layout.width, 200.0);
883 }
884
885 #[test]
886 fn test_compute_grid_layout_twelve_column() {
887 let template = GridTemplate::twelve_column();
888 let layout = compute_grid_layout(&template, 1200.0, 400.0, &[]);
889
890 assert_eq!(layout.columns.len(), 12);
891 }
895
896 #[test]
897 fn test_grid_layout_area_bounds() {
898 let template = GridTemplate::columns([TrackSize::px(100.0), TrackSize::px(100.0)]);
899 let layout = compute_grid_layout(&template, 200.0, 100.0, &[(50.0, 50.0)]);
900
901 let bounds = layout.area_bounds(&GridArea::cell(0, 0));
902 assert!(bounds.is_some());
903 let (x, y, w, _h) = bounds.unwrap();
904 assert_eq!(x, 0.0);
905 assert_eq!(y, 0.0);
906 assert_eq!(w, 100.0);
907 }
908
909 #[test]
910 fn test_grid_layout_area_bounds_span() {
911 let template = GridTemplate::columns([
912 TrackSize::px(100.0),
913 TrackSize::px(100.0),
914 TrackSize::px(100.0),
915 ]);
916 let layout = compute_grid_layout(&template, 300.0, 100.0, &[(50.0, 50.0)]);
917
918 let bounds = layout.area_bounds(&GridArea::row_span(0, 0, 2));
919 assert!(bounds.is_some());
920 let (x, y, w, _h) = bounds.unwrap();
921 assert_eq!(x, 0.0);
922 assert_eq!(y, 0.0);
923 assert_eq!(w, 200.0);
924 }
925
926 #[test]
931 fn test_auto_place_items_simple() {
932 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
933 let items = vec![GridItem::new(), GridItem::new(), GridItem::new()];
934
935 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
936
937 assert_eq!(placements.len(), 3);
938 assert_eq!(placements[0], (0, 0));
939 assert_eq!(placements[1], (0, 1));
940 assert_eq!(placements[2], (1, 0));
941 }
942
943 #[test]
944 fn test_auto_place_items_with_span() {
945 let template =
946 GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0), TrackSize::fr(1.0)]);
947 let items = vec![
948 GridItem::new().span_columns(2),
949 GridItem::new(),
950 GridItem::new(),
951 ];
952
953 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
954
955 assert_eq!(placements.len(), 3);
956 assert_eq!(placements[0], (0, 0)); assert_eq!(placements[1], (0, 2)); assert_eq!(placements[2], (1, 0)); }
960
961 #[test]
962 fn test_auto_place_items_explicit() {
963 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
964 let items = vec![GridItem::new().column(2).row(2), GridItem::new()];
965
966 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
967
968 assert_eq!(placements[0], (1, 1)); assert_eq!(placements[1], (0, 0)); }
971
972 #[test]
973 fn test_auto_place_items_named_area() {
974 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)])
975 .with_area("main", GridArea::cell(1, 1));
976 let items = vec![GridItem::new().in_area("main"), GridItem::new()];
977
978 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
979
980 assert_eq!(placements[0], (1, 1)); assert_eq!(placements[1], (0, 0)); }
983
984 #[test]
985 fn test_auto_place_items_column_flow() {
986 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
987 let items = vec![GridItem::new(), GridItem::new(), GridItem::new()];
988
989 let placements = auto_place_items(&template, &items, GridAutoFlow::Column);
990
991 assert_eq!(placements.len(), 3);
992 assert_eq!(placements[0], (0, 0));
994 assert_eq!(placements[1], (1, 0));
995 assert_eq!(placements[2], (2, 0));
996 }
997
998 #[test]
1003 fn test_grid_auto_flow_default() {
1004 assert_eq!(GridAutoFlow::default(), GridAutoFlow::Row);
1005 }
1006
1007 #[test]
1008 fn test_grid_auto_flow_all_variants() {
1009 assert_eq!(GridAutoFlow::Row, GridAutoFlow::Row);
1010 assert_eq!(GridAutoFlow::Column, GridAutoFlow::Column);
1011 assert_eq!(GridAutoFlow::RowDense, GridAutoFlow::RowDense);
1012 assert_eq!(GridAutoFlow::ColumnDense, GridAutoFlow::ColumnDense);
1013 }
1014
1015 #[test]
1016 fn test_grid_auto_flow_clone() {
1017 let flow = GridAutoFlow::ColumnDense;
1018 let cloned = flow;
1019 assert_eq!(flow, cloned);
1020 }
1021
1022 #[test]
1023 fn test_grid_auto_flow_debug() {
1024 let flow = GridAutoFlow::RowDense;
1025 let debug = format!("{:?}", flow);
1026 assert!(debug.contains("RowDense"));
1027 }
1028
1029 #[test]
1034 fn test_track_size_auto_const() {
1035 assert_eq!(TrackSize::AUTO, TrackSize::Auto);
1036 }
1037
1038 #[test]
1039 fn test_track_size_min_content() {
1040 let size = TrackSize::MinContent;
1041 assert_eq!(size, TrackSize::MinContent);
1042 }
1043
1044 #[test]
1045 fn test_track_size_max_content() {
1046 let size = TrackSize::MaxContent;
1047 assert_eq!(size, TrackSize::MaxContent);
1048 }
1049
1050 #[test]
1051 fn test_track_size_clone() {
1052 let size = TrackSize::Fr(2.5);
1053 let cloned = size;
1054 assert_eq!(size, cloned);
1055 }
1056
1057 #[test]
1058 fn test_track_size_debug() {
1059 let size = TrackSize::Px(100.0);
1060 let debug = format!("{:?}", size);
1061 assert!(debug.contains("Px"));
1062 assert!(debug.contains("100"));
1063 }
1064
1065 #[test]
1066 fn test_track_size_serialize() {
1067 let size = TrackSize::Fr(1.5);
1068 let json = serde_json::to_string(&size).unwrap();
1069 assert!(json.contains("Fr"));
1070 }
1071
1072 #[test]
1073 fn test_track_size_deserialize() {
1074 let json = r#"{"Px":200.0}"#;
1075 let size: TrackSize = serde_json::from_str(json).unwrap();
1076 assert_eq!(size, TrackSize::Px(200.0));
1077 }
1078
1079 #[test]
1084 fn test_grid_template_with_column_gap() {
1085 let template = GridTemplate::new().with_column_gap(20.0);
1086 assert_eq!(template.column_gap, 20.0);
1087 assert_eq!(template.row_gap, 0.0);
1088 }
1089
1090 #[test]
1091 fn test_grid_template_with_row_gap() {
1092 let template = GridTemplate::new().with_row_gap(15.0);
1093 assert_eq!(template.row_gap, 15.0);
1094 assert_eq!(template.column_gap, 0.0);
1095 }
1096
1097 #[test]
1098 fn test_grid_template_column_count() {
1099 let template =
1100 GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0), TrackSize::fr(1.0)]);
1101 assert_eq!(template.column_count(), 3);
1102 }
1103
1104 #[test]
1105 fn test_grid_template_row_count() {
1106 let template = GridTemplate::new().with_rows([TrackSize::px(50.0), TrackSize::px(100.0)]);
1107 assert_eq!(template.row_count(), 2);
1108 }
1109
1110 #[test]
1111 fn test_grid_template_empty_counts() {
1112 let template = GridTemplate::new();
1113 assert_eq!(template.column_count(), 0);
1114 assert_eq!(template.row_count(), 0);
1115 }
1116
1117 #[test]
1118 fn test_grid_template_default() {
1119 let template = GridTemplate::default();
1120 assert!(template.columns.is_empty());
1121 assert!(template.areas.is_empty());
1122 }
1123
1124 #[test]
1125 fn test_grid_template_clone() {
1126 let template = GridTemplate::twelve_column().with_area("header", GridArea::cell(0, 0));
1127 let cloned = template.clone();
1128 assert_eq!(cloned.columns.len(), 12);
1129 assert!(cloned.areas.contains_key("header"));
1130 }
1131
1132 #[test]
1133 fn test_grid_template_serialize() {
1134 let template = GridTemplate::columns([TrackSize::px(100.0)]);
1135 let json = serde_json::to_string(&template).unwrap();
1136 assert!(json.contains("columns"));
1137 }
1138
1139 #[test]
1144 fn test_grid_area_span_counts_zero() {
1145 let area = GridArea::new(5, 5, 5, 5); assert_eq!(area.row_span_count(), 0);
1147 assert_eq!(area.col_span_count(), 0);
1148 }
1149
1150 #[test]
1151 fn test_grid_area_span_counts_saturating() {
1152 let area = GridArea::new(10, 10, 5, 5); assert_eq!(area.row_span_count(), 0);
1154 assert_eq!(area.col_span_count(), 0);
1155 }
1156
1157 #[test]
1158 fn test_grid_area_clone() {
1159 let area = GridArea::new(1, 2, 3, 4);
1160 let cloned = area;
1161 assert_eq!(area, cloned);
1162 }
1163
1164 #[test]
1165 fn test_grid_area_debug() {
1166 let area = GridArea::cell(0, 0);
1167 let debug = format!("{:?}", area);
1168 assert!(debug.contains("GridArea"));
1169 }
1170
1171 #[test]
1172 fn test_grid_area_serialize() {
1173 let area = GridArea::new(0, 0, 2, 4);
1174 let json = serde_json::to_string(&area).unwrap();
1175 assert!(json.contains("row_start"));
1176 }
1177
1178 #[test]
1179 fn test_grid_area_deserialize() {
1180 let json = r#"{"row_start":1,"row_end":3,"col_start":0,"col_end":2}"#;
1181 let area: GridArea = serde_json::from_str(json).unwrap();
1182 assert_eq!(area.row_start, 1);
1183 assert_eq!(area.col_end, 2);
1184 }
1185
1186 #[test]
1191 fn test_grid_item_default() {
1192 let item = GridItem::default();
1193 assert_eq!(item.column_start, 0);
1194 assert_eq!(item.row_start, 0);
1195 assert!(item.area.is_none());
1196 }
1197
1198 #[test]
1199 fn test_grid_item_justify_self() {
1200 let item = GridItem::new().justify_self(GridAlign::End);
1201 assert_eq!(item.justify_self, Some(GridAlign::End));
1202 }
1203
1204 #[test]
1205 fn test_grid_item_align_self() {
1206 let item = GridItem::new().align_self(GridAlign::Stretch);
1207 assert_eq!(item.align_self, Some(GridAlign::Stretch));
1208 }
1209
1210 #[test]
1211 fn test_grid_item_effective_row_span() {
1212 let item = GridItem::new().span_rows(3);
1213 assert_eq!(item.effective_row_span(), 3);
1214
1215 let mut item2 = GridItem::new();
1216 item2.row_start = 1;
1217 item2.row_end = 5;
1218 assert_eq!(item2.effective_row_span(), 4);
1219 }
1220
1221 #[test]
1222 fn test_grid_item_effective_span_minimum() {
1223 let mut item = GridItem::new();
1224 item.column_span = 0; assert_eq!(item.effective_column_span(), 1); item.row_span = 0;
1228 assert_eq!(item.effective_row_span(), 1); }
1230
1231 #[test]
1232 fn test_grid_item_clone() {
1233 let item = GridItem::new().column(2).row(3).span_columns(2);
1234 let cloned = item.clone();
1235 assert_eq!(cloned.column_start, 2);
1236 assert_eq!(cloned.row_start, 3);
1237 }
1238
1239 #[test]
1240 fn test_grid_item_serialize() {
1241 let item = GridItem::new().column(1).row(1);
1242 let json = serde_json::to_string(&item).unwrap();
1243 assert!(json.contains("column_start"));
1244 }
1245
1246 #[test]
1251 fn test_grid_align_all_variants() {
1252 assert_eq!(GridAlign::Start, GridAlign::Start);
1253 assert_eq!(GridAlign::End, GridAlign::End);
1254 assert_eq!(GridAlign::Center, GridAlign::Center);
1255 assert_eq!(GridAlign::Stretch, GridAlign::Stretch);
1256 }
1257
1258 #[test]
1259 fn test_grid_align_clone() {
1260 let align = GridAlign::Stretch;
1261 let cloned = align;
1262 assert_eq!(align, cloned);
1263 }
1264
1265 #[test]
1266 fn test_grid_align_debug() {
1267 let align = GridAlign::Start;
1268 let debug = format!("{:?}", align);
1269 assert!(debug.contains("Start"));
1270 }
1271
1272 #[test]
1273 fn test_grid_align_serialize() {
1274 let align = GridAlign::End;
1275 let json = serde_json::to_string(&align).unwrap();
1276 assert!(json.contains("End"));
1277 }
1278
1279 #[test]
1284 fn test_grid_layout_default() {
1285 let layout = GridLayout::default();
1286 assert!(layout.columns.is_empty());
1287 assert!(layout.rows.is_empty());
1288 assert_eq!(layout.width, 0.0);
1289 assert_eq!(layout.height, 0.0);
1290 }
1291
1292 #[test]
1293 fn test_grid_layout_area_bounds_out_of_range() {
1294 let layout = GridLayout {
1295 columns: vec![(0.0, 100.0)],
1296 rows: vec![(0.0, 50.0)],
1297 width: 100.0,
1298 height: 50.0,
1299 };
1300
1301 let bounds = layout.area_bounds(&GridArea::cell(10, 10));
1303 assert!(bounds.is_none());
1304 }
1305
1306 #[test]
1307 fn test_grid_layout_area_bounds_partial_out_of_range() {
1308 let layout = GridLayout {
1309 columns: vec![(0.0, 100.0), (100.0, 100.0)],
1310 rows: vec![(0.0, 50.0)],
1311 width: 200.0,
1312 height: 50.0,
1313 };
1314
1315 let bounds = layout.area_bounds(&GridArea::new(0, 0, 5, 5));
1317 assert!(bounds.is_some());
1318 let (_, _, w, _) = bounds.unwrap();
1319 assert_eq!(w, 200.0); }
1321
1322 #[test]
1323 fn test_grid_layout_item_bounds() {
1324 let layout = GridLayout {
1325 columns: vec![(0.0, 100.0), (100.0, 100.0)],
1326 rows: vec![(0.0, 50.0), (50.0, 50.0)],
1327 width: 200.0,
1328 height: 100.0,
1329 };
1330
1331 let item = GridItem::new().span_columns(2);
1332 let bounds = layout.item_bounds(&item, 0, 0);
1333 assert!(bounds.is_some());
1334 let (x, y, w, h) = bounds.unwrap();
1335 assert_eq!(x, 0.0);
1336 assert_eq!(y, 0.0);
1337 assert_eq!(w, 200.0);
1338 assert_eq!(h, 50.0);
1339 }
1340
1341 #[test]
1342 fn test_grid_layout_clone() {
1343 let layout = GridLayout {
1344 columns: vec![(0.0, 100.0)],
1345 rows: vec![(0.0, 50.0)],
1346 width: 100.0,
1347 height: 50.0,
1348 };
1349 let cloned = layout.clone();
1350 assert_eq!(cloned.width, 100.0);
1351 }
1352
1353 #[test]
1354 fn test_grid_layout_debug() {
1355 let layout = GridLayout::default();
1356 let debug = format!("{:?}", layout);
1357 assert!(debug.contains("GridLayout"));
1358 }
1359
1360 #[test]
1365 fn test_compute_track_sizes_all_fr() {
1366 let tracks = vec![TrackSize::Fr(1.0), TrackSize::Fr(2.0), TrackSize::Fr(1.0)];
1367 let result = compute_track_sizes(&tracks, 400.0, 0.0, &[]);
1368
1369 assert_eq!(result.len(), 3);
1370 assert_eq!(result[0].1, 100.0); assert_eq!(result[1].1, 200.0); assert_eq!(result[2].1, 100.0); }
1374
1375 #[test]
1376 fn test_compute_track_sizes_zero_fr() {
1377 let tracks = vec![TrackSize::Fr(0.0), TrackSize::Fr(1.0)];
1378 let result = compute_track_sizes(&tracks, 200.0, 0.0, &[]);
1379
1380 assert_eq!(result.len(), 2);
1381 assert_eq!(result[0].1, 0.0);
1382 assert_eq!(result[1].1, 200.0);
1383 }
1384
1385 #[test]
1386 fn test_compute_track_sizes_insufficient_space() {
1387 let tracks = vec![TrackSize::Px(300.0), TrackSize::Fr(1.0)];
1388 let result = compute_track_sizes(&tracks, 200.0, 0.0, &[]);
1389
1390 assert_eq!(result[0].1, 300.0);
1392 assert_eq!(result[1].1, 0.0);
1393 }
1394
1395 #[test]
1396 fn test_compute_track_sizes_large_gap() {
1397 let tracks = vec![TrackSize::Fr(1.0), TrackSize::Fr(1.0)];
1398 let result = compute_track_sizes(&tracks, 100.0, 200.0, &[]);
1399
1400 assert!(result[0].1 <= 0.0);
1402 }
1403
1404 #[test]
1405 fn test_compute_track_sizes_min_content() {
1406 let tracks = vec![TrackSize::MinContent, TrackSize::Fr(1.0)];
1407 let content = vec![60.0, 0.0];
1408 let result = compute_track_sizes(&tracks, 200.0, 0.0, &content);
1409
1410 assert_eq!(result[0].1, 60.0);
1411 assert_eq!(result[1].1, 140.0);
1412 }
1413
1414 #[test]
1415 fn test_compute_track_sizes_max_content() {
1416 let tracks = vec![TrackSize::MaxContent, TrackSize::Fr(1.0)];
1417 let content = vec![80.0];
1418 let result = compute_track_sizes(&tracks, 200.0, 0.0, &content);
1419
1420 assert_eq!(result[0].1, 80.0);
1421 assert_eq!(result[1].1, 120.0);
1422 }
1423
1424 #[test]
1429 fn test_compute_grid_layout_empty_template() {
1430 let template = GridTemplate::new();
1431 let layout = compute_grid_layout(&template, 200.0, 100.0, &[]);
1432
1433 assert!(layout.columns.is_empty());
1434 assert_eq!(layout.width, 0.0);
1435 }
1436
1437 #[test]
1438 fn test_compute_grid_layout_auto_rows() {
1439 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
1440 let children = vec![(50.0, 30.0), (50.0, 30.0), (50.0, 30.0), (50.0, 30.0)];
1441 let layout = compute_grid_layout(&template, 200.0, 200.0, &children);
1442
1443 assert_eq!(layout.rows.len(), 2);
1445 }
1446
1447 #[test]
1448 fn test_compute_grid_layout_with_gaps() {
1449 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)])
1450 .with_rows([TrackSize::px(50.0)])
1451 .with_gap(10.0);
1452 let layout = compute_grid_layout(&template, 210.0, 50.0, &[]);
1453
1454 let col1_end = layout.columns[0].0 + layout.columns[0].1;
1456 let col2_start = layout.columns[1].0;
1457 assert!((col2_start - col1_end - 10.0).abs() < 0.01);
1458 }
1459
1460 #[test]
1465 fn test_auto_place_items_empty() {
1466 let template = GridTemplate::columns([TrackSize::fr(1.0)]);
1467 let placements = auto_place_items(&template, &[], GridAutoFlow::Row);
1468 assert!(placements.is_empty());
1469 }
1470
1471 #[test]
1472 fn test_auto_place_items_single_column() {
1473 let template = GridTemplate::columns([TrackSize::fr(1.0)]);
1474 let items = vec![GridItem::new(), GridItem::new(), GridItem::new()];
1475 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
1476
1477 assert_eq!(placements[0], (0, 0));
1478 assert_eq!(placements[1], (1, 0));
1479 assert_eq!(placements[2], (2, 0));
1480 }
1481
1482 #[test]
1483 fn test_auto_place_items_span_exceeds_grid() {
1484 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
1485 let items = vec![GridItem::new().span_columns(5)]; let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
1487
1488 assert_eq!(placements.len(), 1);
1490 }
1491
1492 #[test]
1493 fn test_auto_place_items_row_span() {
1494 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
1495 let items = vec![
1496 GridItem::new().span_rows(2),
1497 GridItem::new(),
1498 GridItem::new(),
1499 ];
1500 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
1501
1502 assert_eq!(placements[0], (0, 0));
1504 assert_eq!(placements[1], (0, 1));
1505 }
1506
1507 #[test]
1508 fn test_auto_place_items_missing_area() {
1509 let template = GridTemplate::columns([TrackSize::fr(1.0)]);
1510 let items = vec![GridItem::new().in_area("nonexistent")];
1511 let placements = auto_place_items(&template, &items, GridAutoFlow::Row);
1512
1513 assert_eq!(placements[0], (0, 0));
1515 }
1516
1517 #[test]
1518 fn test_auto_place_items_column_dense() {
1519 let template = GridTemplate::columns([TrackSize::fr(1.0), TrackSize::fr(1.0)]);
1520 let items = vec![GridItem::new(), GridItem::new()];
1521 let placements = auto_place_items(&template, &items, GridAutoFlow::ColumnDense);
1522
1523 assert_eq!(placements.len(), 2);
1525 }
1526}