1use crate::geometry::{AbsoluteAxis, AbstractAxis, InBothAbsAxis};
4use crate::geometry::{Line, Point, Rect, Size};
5use crate::style::{AlignItems, AlignSelf, AvailableSpace, Overflow, Position};
6use crate::tree::{Layout, LayoutInput, LayoutOutput, LayoutPartialTreeExt, NodeId, RunMode, SizingMode};
7use crate::util::debug::debug_log;
8use crate::util::sys::{f32_max, f32_min, GridTrackVec, Vec};
9use crate::util::MaybeMath;
10use crate::util::{MaybeResolve, ResolveOrZero};
11use crate::{
12 style_helpers::*, AlignContent, BoxGenerationMode, BoxSizing, CoreStyle, Direction, GridContainerStyle,
13 GridItemStyle, JustifyContent, LayoutGridContainer, RequestedAxis,
14};
15use alignment::{align_and_position_item, align_tracks};
16use explicit_grid::{compute_explicit_grid_size_in_axis, initialize_grid_tracks, AutoRepeatStrategy};
17use implicit_grid::compute_grid_size_estimate;
18use placement::place_grid_items;
19use track_sizing::{
20 determine_if_item_crosses_flexible_or_intrinsic_tracks, resolve_item_track_indexes, track_sizing_algorithm,
21};
22use types::{CellOccupancyMatrix, GridTrack, NamedLineResolver, TrackCounts};
23
24#[cfg(feature = "detailed_layout_info")]
25use types::{GridItem, GridTrackKind};
26
27pub(crate) use types::{GridCoordinate, GridLine, OriginZeroLine, MAX_GRID_TRACKS, MAX_OZ_LINE, MIN_OZ_LINE};
28
29mod alignment;
30mod explicit_grid;
31mod implicit_grid;
32mod placement;
33mod track_sizing;
34mod types;
35mod util;
36
37pub fn compute_grid_layout<Tree: LayoutGridContainer>(
44 tree: &mut Tree,
45 node: NodeId,
46 inputs: LayoutInput,
47) -> LayoutOutput {
48 let LayoutInput { known_dimensions, parent_size, available_space, run_mode, .. } = inputs;
49
50 let style = tree.get_grid_container_style(node);
51 let direction = style.direction();
52
53 let aspect_ratio = style.aspect_ratio();
56 let padding = style.padding().resolve_or_zero(parent_size.width, |val, basis| tree.calc(val, basis));
57 let border = style.border().resolve_or_zero(parent_size.width, |val, basis| tree.calc(val, basis));
58 let padding_border = padding + border;
59 let padding_border_size = padding_border.sum_axes();
60 let box_sizing_adjustment =
61 if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
62
63 let min_size = style
64 .min_size()
65 .maybe_resolve(parent_size, |val, basis| tree.calc(val, basis))
66 .maybe_apply_aspect_ratio(aspect_ratio)
67 .maybe_add(box_sizing_adjustment);
68 let max_size = style
69 .max_size()
70 .maybe_resolve(parent_size, |val, basis| tree.calc(val, basis))
71 .maybe_apply_aspect_ratio(aspect_ratio)
72 .maybe_add(box_sizing_adjustment);
73 let preferred_size = if inputs.sizing_mode == SizingMode::InherentSize {
74 style
75 .size()
76 .maybe_resolve(parent_size, |val, basis| tree.calc(val, basis))
77 .maybe_apply_aspect_ratio(style.aspect_ratio())
78 .maybe_add(box_sizing_adjustment)
79 } else {
80 Size::NONE
81 };
82
83 let scrollbar_gutter = style.overflow().transpose().map(|overflow| match overflow {
87 Overflow::Scroll => style.scrollbar_width(),
88 _ => 0.0,
89 });
90 let mut content_box_inset = padding_border;
91 content_box_inset.bottom += scrollbar_gutter.y;
92
93 match direction {
94 Direction::Ltr => content_box_inset.right += scrollbar_gutter.x,
95 Direction::Rtl => content_box_inset.left += scrollbar_gutter.x,
96 };
97
98 let align_content = style.align_content().unwrap_or(AlignContent::STRETCH);
99 let justify_content = style.justify_content().unwrap_or(JustifyContent::STRETCH);
100 let align_items = style.align_items();
101 let justify_items = style.justify_items();
102
103 let grid_template_columns = style.grid_template_columns();
106 let grid_template_rows = style.grid_template_rows();
107 let grid_auto_columns = style.grid_auto_columns();
108 let grid_auto_rows = style.grid_auto_rows();
109
110 let constrained_available_space = known_dimensions
111 .or(preferred_size)
112 .map(|size| size.map(AvailableSpace::Definite))
113 .unwrap_or(available_space)
114 .maybe_clamp(min_size, max_size)
115 .maybe_max(padding_border_size);
116
117 let available_grid_space = Size {
118 width: constrained_available_space
119 .width
120 .map_definite_value(|space| space - content_box_inset.horizontal_axis_sum()),
121 height: constrained_available_space
122 .height
123 .map_definite_value(|space| space - content_box_inset.vertical_axis_sum()),
124 };
125
126 let outer_node_size =
127 known_dimensions.or(preferred_size).maybe_clamp(min_size, max_size).maybe_max(padding_border_size);
128
129 let inner_min_size = min_size.maybe_sub(content_box_inset.sum_axes());
132 let inner_max_size = max_size.maybe_sub(content_box_inset.sum_axes());
133 let mut inner_node_size = Size {
134 width: outer_node_size.width.map(|space| space - content_box_inset.horizontal_axis_sum()),
135 height: outer_node_size.height.map(|space| space - content_box_inset.vertical_axis_sum()),
136 };
137
138 debug_log!("parent_size", dbg:parent_size);
139 debug_log!("outer_node_size", dbg:outer_node_size);
140 debug_log!("inner_node_size", dbg:inner_node_size);
141
142 if run_mode == RunMode::ComputeSize {
145 if let Size { width: Some(width), height: Some(height) } = outer_node_size {
146 return LayoutOutput::from_outer_size(Size { width, height });
147 }
148
149 if inputs.axis == RequestedAxis::Horizontal {
151 if let Some(width) = outer_node_size.width {
152 return LayoutOutput::from_outer_size(Size { width, height: 0.0 });
153 }
154 }
155 }
156
157 let get_child_styles_iter = |node| {
160 tree.child_ids(node).map(|child_node: NodeId| tree.get_grid_child_style(child_node)).filter(|style| {
161 style.box_generation_mode() != BoxGenerationMode::None && style.position() != Position::Absolute
162 })
163 };
164 let child_styles_iter = get_child_styles_iter(node);
165
166 let auto_fit_container_size = outer_node_size
171 .or(max_size)
172 .or(min_size)
173 .maybe_clamp(min_size, max_size)
174 .maybe_max(padding_border_size)
175 .maybe_sub(content_box_inset.sum_axes());
176
177 let auto_repeat_fit_strategy = outer_node_size.or(max_size).map(|val| match val {
184 Some(_) => AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
185 None => AutoRepeatStrategy::MinRepetitionsThatDoOverflow,
186 });
187
188 let (col_auto_repetition_count, grid_template_col_count) = compute_explicit_grid_size_in_axis(
191 &style,
192 auto_fit_container_size.width,
193 auto_repeat_fit_strategy.width,
194 |val, basis| tree.calc(val, basis),
195 AbsoluteAxis::Horizontal,
196 );
197 let (row_auto_repetition_count, grid_template_row_count) = compute_explicit_grid_size_in_axis(
198 &style,
199 auto_fit_container_size.height,
200 auto_repeat_fit_strategy.height,
201 |val, basis| tree.calc(val, basis),
202 AbsoluteAxis::Vertical,
203 );
204
205 let mut name_resolver = NamedLineResolver::new(&style, col_auto_repetition_count, row_auto_repetition_count);
207
208 let explicit_col_count = grid_template_col_count.max(name_resolver.area_column_count()).min(MAX_GRID_TRACKS);
211 let explicit_row_count = grid_template_row_count.max(name_resolver.area_row_count()).min(MAX_GRID_TRACKS);
212
213 name_resolver.set_explicit_column_count(explicit_col_count);
214 name_resolver.set_explicit_row_count(explicit_row_count);
215
216 let (est_col_counts, est_row_counts) =
220 compute_grid_size_estimate(explicit_col_count, explicit_row_count, direction, child_styles_iter);
221
222 let mut items = Vec::with_capacity(tree.child_count(node));
225 let mut cell_occupancy_matrix = CellOccupancyMatrix::with_track_counts(est_col_counts, est_row_counts);
226 let in_flow_children_iter = || {
227 tree.child_ids(node)
228 .enumerate()
229 .map(|(index, child_node)| (index, child_node, tree.get_grid_child_style(child_node)))
230 .filter(|(_, _, style)| {
231 style.box_generation_mode() != BoxGenerationMode::None && style.position() != Position::Absolute
232 })
233 };
234 place_grid_items(
235 &mut cell_occupancy_matrix,
236 &mut items,
237 in_flow_children_iter,
238 direction,
239 style.grid_auto_flow(),
240 align_items.unwrap_or(AlignItems::STRETCH),
241 justify_items.unwrap_or(AlignItems::STRETCH),
242 &name_resolver,
243 );
244
245 let final_col_counts = *cell_occupancy_matrix.track_counts(AbsoluteAxis::Horizontal);
247 let final_row_counts = *cell_occupancy_matrix.track_counts(AbsoluteAxis::Vertical);
248
249 let mut columns = GridTrackVec::new();
253 let mut rows = GridTrackVec::new();
254 let mut column_track_counts_for_init = final_col_counts;
255 if direction.is_rtl() && final_col_counts.explicit <= 1 {
256 column_track_counts_for_init.negative_implicit = final_col_counts.positive_implicit;
257 column_track_counts_for_init.positive_implicit = final_col_counts.negative_implicit;
258 }
259 initialize_grid_tracks(
260 &mut columns,
261 column_track_counts_for_init,
262 &style,
263 AbsoluteAxis::Horizontal,
264 col_auto_repetition_count,
265 |column_index| {
266 let occupancy_index = if direction.is_rtl() {
267 rtl_column_occupancy_index_for_initialization(column_index, final_col_counts)
268 } else {
269 column_index
270 };
271 cell_occupancy_matrix.column_is_occupied(occupancy_index)
272 },
273 );
274 initialize_grid_tracks(
275 &mut rows,
276 final_row_counts,
277 &style,
278 AbsoluteAxis::Vertical,
279 row_auto_repetition_count,
280 |row_index| cell_occupancy_matrix.row_is_occupied(row_index),
281 );
282 if direction.is_rtl() {
283 reverse_non_gutter_tracks(&mut columns, final_col_counts);
284 }
285
286 drop(grid_template_rows);
287 drop(grid_template_columns);
288 drop(grid_auto_rows);
289 drop(grid_auto_columns);
290 drop(style);
291
292 resolve_item_track_indexes(&mut items, final_col_counts, final_row_counts);
298 determine_if_item_crosses_flexible_or_intrinsic_tracks(&mut items, &columns, &rows);
301
302 let has_baseline_aligned_item = items.iter().any(|item| item.align_self == AlignSelf::BASELINE);
304
305 track_sizing_algorithm(
307 tree,
308 AbstractAxis::Inline,
309 inner_min_size.get(AbstractAxis::Inline),
310 inner_max_size.get(AbstractAxis::Inline),
311 justify_content,
312 align_content,
313 available_grid_space,
314 inner_node_size,
315 &mut columns,
316 &mut rows,
317 &mut items,
318 |track: &GridTrack, parent_size: Option<f32>, tree: &Tree| {
319 track.max_track_sizing_function.definite_value(parent_size, |val, basis| tree.calc(val, basis))
320 },
321 has_baseline_aligned_item,
322 );
323 let initial_column_sum = columns.iter().map(|track| track.base_size).sum::<f32>();
324 inner_node_size.width = inner_node_size.width.or_else(|| initial_column_sum.into());
325
326 items.iter_mut().for_each(|item| item.grid_area_size_cache = None);
327
328 track_sizing_algorithm(
330 tree,
331 AbstractAxis::Block,
332 inner_min_size.get(AbstractAxis::Block),
333 inner_max_size.get(AbstractAxis::Block),
334 align_content,
335 justify_content,
336 available_grid_space,
337 inner_node_size,
338 &mut rows,
339 &mut columns,
340 &mut items,
341 |track: &GridTrack, _, _| Some(track.base_size),
342 false, );
344 let initial_row_sum = rows.iter().map(|track| track.base_size).sum::<f32>();
345 inner_node_size.height = inner_node_size.height.or_else(|| initial_row_sum.into());
346
347 debug_log!("initial_column_sum", dbg:initial_column_sum);
348 debug_log!(dbg: columns.iter().map(|track| track.base_size).collect::<Vec<_>>());
349 debug_log!("initial_row_sum", dbg:initial_row_sum);
350 debug_log!(dbg: rows.iter().map(|track| track.base_size).collect::<Vec<_>>());
351
352 let resolved_style_size = known_dimensions.or(preferred_size);
354 let mut container_border_box = Size {
355 width: resolved_style_size
356 .get(AbstractAxis::Inline)
357 .unwrap_or_else(|| initial_column_sum + content_box_inset.horizontal_axis_sum())
358 .maybe_clamp(min_size.width, max_size.width)
359 .max(padding_border_size.width),
360 height: resolved_style_size
361 .get(AbstractAxis::Block)
362 .unwrap_or_else(|| initial_row_sum + content_box_inset.vertical_axis_sum())
363 .maybe_clamp(min_size.height, max_size.height)
364 .max(padding_border_size.height),
365 };
366 let mut container_content_box = Size {
367 width: f32_max(0.0, container_border_box.width - content_box_inset.horizontal_axis_sum()),
368 height: f32_max(0.0, container_border_box.height - content_box_inset.vertical_axis_sum()),
369 };
370
371 if run_mode == RunMode::ComputeSize {
373 return LayoutOutput::from_outer_size(container_border_box);
374 }
375
376 if !available_grid_space.width.is_definite() {
380 for column in &mut columns {
381 let min: Option<f32> = column
382 .min_track_sizing_function
383 .resolved_percentage_size(container_content_box.width, |val, basis| tree.calc(val, basis));
384 let max: Option<f32> = column
385 .max_track_sizing_function
386 .resolved_percentage_size(container_content_box.width, |val, basis| tree.calc(val, basis));
387 column.base_size = column.base_size.maybe_clamp(min, max);
388 }
389 }
390 if !available_grid_space.height.is_definite() {
391 for row in &mut rows {
392 let min: Option<f32> = row
393 .min_track_sizing_function
394 .resolved_percentage_size(container_content_box.height, |val, basis| tree.calc(val, basis));
395 let max: Option<f32> = row
396 .max_track_sizing_function
397 .resolved_percentage_size(container_content_box.height, |val, basis| tree.calc(val, basis));
398 row.base_size = row.base_size.maybe_clamp(min, max);
399 }
400 }
401
402 let mut rerun_column_sizing;
407 let mut intrinsic_column_contribution_changed = false;
408
409 let has_percentage_column = columns.iter().any(|track| track.uses_percentage());
410 let has_percentage_row = rows.iter().any(|track| track.uses_percentage());
411 let parent_width_indefinite = !available_space.width.is_definite();
412 rerun_column_sizing = parent_width_indefinite && has_percentage_column;
413
414 if !rerun_column_sizing {
415 intrinsic_column_contribution_changed =
416 items.iter_mut().filter(|item| item.crosses_intrinsic_column).any(|item| {
417 let grid_area_size = item.grid_area_size(
418 AbstractAxis::Inline,
419 &columns,
420 &rows,
421 inner_node_size,
422 |track: &GridTrack, _| Some(track.base_size),
423 &|val, basis| tree.calc(val, basis),
424 );
425 let available_space = grid_area_size.with(AbstractAxis::Inline, None);
426 let new_min_content_contribution =
427 item.min_content_contribution(AbstractAxis::Inline, tree, grid_area_size, available_space);
428
429 let has_changed = Some(new_min_content_contribution) != item.min_content_contribution_cache.width;
430
431 item.grid_area_size_cache = Some(grid_area_size);
432 item.min_content_contribution_cache.width = Some(new_min_content_contribution);
433 item.max_content_contribution_cache.width = None;
434 item.minimum_contribution_cache.width = None;
435
436 has_changed
437 });
438 rerun_column_sizing = intrinsic_column_contribution_changed;
439 } else {
440 items.iter_mut().for_each(|item| {
442 item.grid_area_size_cache = None;
443 item.min_content_contribution_cache.width = None;
444 item.max_content_contribution_cache.width = None;
445 item.minimum_contribution_cache.width = None;
446 });
447 }
448
449 let mut intrinsic_row_contribution_changed = false;
450
451 if rerun_column_sizing {
452 track_sizing_algorithm(
454 tree,
455 AbstractAxis::Inline,
456 inner_min_size.get(AbstractAxis::Inline),
457 inner_max_size.get(AbstractAxis::Inline),
458 justify_content,
459 align_content,
460 available_grid_space,
461 inner_node_size,
462 &mut columns,
463 &mut rows,
464 &mut items,
465 |track: &GridTrack, _, _| Some(track.base_size),
466 has_baseline_aligned_item,
467 );
468
469 let mut rerun_row_sizing;
474
475 let parent_height_indefinite = !available_space.height.is_definite();
476 rerun_row_sizing = parent_height_indefinite && has_percentage_row;
477
478 if !rerun_row_sizing {
479 intrinsic_row_contribution_changed =
480 items.iter_mut().filter(|item| item.crosses_intrinsic_column).any(|item| {
481 let grid_area_size = item.grid_area_size(
482 AbstractAxis::Block,
483 &rows,
484 &columns,
485 inner_node_size,
486 |track: &GridTrack, _| Some(track.base_size),
487 &|val, basis| tree.calc(val, basis),
488 );
489 let available_space = grid_area_size.with(AbstractAxis::Block, None);
490 let new_min_content_contribution =
491 item.min_content_contribution(AbstractAxis::Block, tree, grid_area_size, available_space);
492
493 let has_changed = Some(new_min_content_contribution) != item.min_content_contribution_cache.height;
494
495 item.grid_area_size_cache = Some(grid_area_size);
496 item.min_content_contribution_cache.height = Some(new_min_content_contribution);
497 item.max_content_contribution_cache.height = None;
498 item.minimum_contribution_cache.height = None;
499
500 has_changed
501 });
502 rerun_row_sizing = intrinsic_row_contribution_changed;
503 } else {
504 items.iter_mut().for_each(|item| {
505 item.grid_area_size_cache = None;
507 item.min_content_contribution_cache.height = None;
508 item.max_content_contribution_cache.height = None;
509 item.minimum_contribution_cache.height = None;
510 });
511 }
512
513 if rerun_row_sizing {
514 track_sizing_algorithm(
516 tree,
517 AbstractAxis::Block,
518 inner_min_size.get(AbstractAxis::Block),
519 inner_max_size.get(AbstractAxis::Block),
520 align_content,
521 justify_content,
522 available_grid_space,
523 inner_node_size,
524 &mut rows,
525 &mut columns,
526 &mut items,
527 |track: &GridTrack, _, _| Some(track.base_size),
528 false, );
530 }
531 }
532
533 if (intrinsic_column_contribution_changed && !has_percentage_column)
534 || (intrinsic_row_contribution_changed && !has_percentage_row)
535 {
536 let final_column_sum = columns.iter().map(|track| track.base_size).sum::<f32>();
537 let final_row_sum = rows.iter().map(|track| track.base_size).sum::<f32>();
538
539 if intrinsic_column_contribution_changed && !has_percentage_column {
540 container_border_box.width = resolved_style_size
541 .get(AbstractAxis::Inline)
542 .unwrap_or_else(|| final_column_sum + content_box_inset.horizontal_axis_sum())
543 .maybe_clamp(min_size.width, max_size.width)
544 .max(padding_border_size.width);
545 container_content_box.width =
546 f32_max(0.0, container_border_box.width - content_box_inset.horizontal_axis_sum());
547 }
548
549 if intrinsic_row_contribution_changed && !has_percentage_row {
550 container_border_box.height = resolved_style_size
551 .get(AbstractAxis::Block)
552 .unwrap_or_else(|| final_row_sum + content_box_inset.vertical_axis_sum())
553 .maybe_clamp(min_size.height, max_size.height)
554 .max(padding_border_size.height);
555 container_content_box.height =
556 f32_max(0.0, container_border_box.height - content_box_inset.vertical_axis_sum());
557 }
558 }
559
560 if run_mode == RunMode::ComputeSize {
562 return LayoutOutput::from_outer_size(container_border_box);
563 }
564
565 let inline_size_without_scrollbar = f32_max(container_border_box.width - padding_border_size.width, 0.0);
569 let inline_scrollbar_gutter_for_alignment = f32_min(scrollbar_gutter.x, inline_size_without_scrollbar);
570 align_tracks(
571 container_content_box.get(AbstractAxis::Inline),
572 Line {
573 start: padding.left + if direction.is_rtl() { inline_scrollbar_gutter_for_alignment } else { 0.0 },
574 end: padding.right + if direction.is_rtl() { 0.0 } else { inline_scrollbar_gutter_for_alignment },
575 },
576 Line { start: border.left, end: border.right },
577 &mut columns,
578 justify_content,
579 direction.is_rtl(),
580 );
581 align_tracks(
583 container_content_box.get(AbstractAxis::Block),
584 Line { start: padding.top, end: padding.bottom },
585 Line { start: border.top, end: border.bottom },
586 &mut rows,
587 align_content,
588 false,
589 );
590
591 #[cfg_attr(not(feature = "content_size"), allow(unused_mut))]
594 let mut item_content_size_contribution = Size::ZERO;
595 #[cfg_attr(not(feature = "content_size"), allow(unused_mut, unused))]
596 let mut absolute_content_size = Size::ZERO;
597
598 items.sort_by_key(|item| item.source_order);
600
601 let container_alignment_styles = InBothAbsAxis { horizontal: justify_items, vertical: align_items };
602
603 for (index, item) in items.iter_mut().enumerate() {
605 let grid_area = Rect {
606 top: rows[item.row_indexes.start as usize + 1].offset,
607 bottom: rows[item.row_indexes.end as usize].offset,
608 left: columns[item.column_indexes.start as usize + 1].offset,
609 right: columns[item.column_indexes.end as usize].offset,
610 };
611 #[cfg_attr(not(feature = "content_size"), allow(unused_variables))]
612 let (content_size_contribution, y_position, height) = align_and_position_item(
613 tree,
614 item.node,
615 index as u32,
616 grid_area,
617 container_alignment_styles,
618 item.baseline_shim,
619 direction,
620 container_border_box.width,
621 border,
622 );
623 item.y_position = y_position;
624 item.height = height;
625
626 #[cfg(feature = "content_size")]
627 {
628 item_content_size_contribution = item_content_size_contribution.f32_max(content_size_contribution);
629 }
630 }
631
632 let mut order = items.len() as u32;
634 (0..tree.child_count(node)).for_each(|index| {
635 let child = tree.get_child_id(node, index);
636 let child_style = tree.get_grid_child_style(child);
637
638 if child_style.box_generation_mode() == BoxGenerationMode::None {
640 drop(child_style);
641 tree.set_unrounded_layout(child, &Layout::with_order(order));
642 tree.perform_child_layout(
643 child,
644 Size::NONE,
645 Size::NONE,
646 Size::MAX_CONTENT,
647 SizingMode::InherentSize,
648 Line::FALSE,
649 );
650 order += 1;
651 return;
652 }
653
654 if child_style.position() == Position::Absolute {
656 let maybe_col_indexes = name_resolver
659 .resolve_column_names(&child_style.grid_column())
660 .into_origin_zero(final_col_counts.explicit)
661 .resolve_absolutely_positioned_grid_tracks()
662 .map(|maybe_grid_line| {
663 maybe_grid_line
664 .map(|line: OriginZeroLine| {
665 if direction.is_rtl() {
666 OriginZeroLine(final_col_counts.explicit as i16 - line.0)
667 } else {
668 line
669 }
670 })
671 .and_then(|line| line.try_into_track_vec_index(final_col_counts))
672 });
673 let maybe_col_indexes = if direction.is_rtl() {
674 Line { start: maybe_col_indexes.end, end: maybe_col_indexes.start }
675 } else {
676 maybe_col_indexes
677 };
678 let maybe_row_indexes = name_resolver
681 .resolve_row_names(&child_style.grid_row())
682 .into_origin_zero(final_row_counts.explicit)
683 .resolve_absolutely_positioned_grid_tracks()
684 .map(|maybe_grid_line| {
685 maybe_grid_line.and_then(|line: OriginZeroLine| line.try_into_track_vec_index(final_row_counts))
686 });
687
688 fn line_as_start_edge(tracks: &[GridTrack], index: usize) -> f32 {
695 tracks.get(index + 1).unwrap_or(&tracks[index]).offset
696 }
697 fn line_as_end_edge(tracks: &[GridTrack], index: usize) -> f32 {
699 if index == 0 {
700 tracks.get(1).unwrap_or(&tracks[0]).offset
701 } else {
702 tracks[index].offset
703 }
704 }
705
706 let grid_area = Rect {
707 top: maybe_row_indexes.start.map(|index| line_as_start_edge(&rows, index)).unwrap_or(border.top),
708 bottom: maybe_row_indexes
709 .end
710 .map(|index| line_as_end_edge(&rows, index))
711 .unwrap_or(container_border_box.height - border.bottom - scrollbar_gutter.y),
712 left: maybe_col_indexes.start.map(|index| line_as_start_edge(&columns, index)).unwrap_or_else(|| {
713 if direction.is_rtl() {
714 border.left + scrollbar_gutter.x
715 } else {
716 border.left
717 }
718 }),
719 right: maybe_col_indexes.end.map(|index| line_as_end_edge(&columns, index)).unwrap_or_else(|| {
720 if direction.is_rtl() {
721 container_border_box.width - border.right
722 } else {
723 container_border_box.width - border.right - scrollbar_gutter.x
724 }
725 }),
726 };
727 drop(child_style);
728
729 #[cfg_attr(not(feature = "content_size"), allow(unused_variables))]
731 let (content_size_contribution, _, _) = align_and_position_item(
732 tree,
733 child,
734 order,
735 grid_area,
736 container_alignment_styles,
737 0.0,
738 direction,
739 container_border_box.width,
740 border,
741 );
742 #[cfg(feature = "content_size")]
743 {
744 absolute_content_size = absolute_content_size.f32_max(content_size_contribution);
745 }
746
747 order += 1;
748 }
749 });
750
751 #[cfg(feature = "detailed_layout_info")]
753 tree.set_detailed_grid_info(
754 node,
755 DetailedGridInfo {
756 rows: DetailedGridTracksInfo::from_grid_tracks_and_track_count(final_row_counts, rows),
757 columns: DetailedGridTracksInfo::from_grid_tracks_and_track_count(final_col_counts, columns),
758 items: items.iter().map(DetailedGridItemsInfo::from_grid_item).collect(),
759 },
760 );
761
762 if items.is_empty() {
764 return LayoutOutput::from_outer_size(container_border_box);
765 }
766
767 let grid_container_baseline: f32 = {
769 items.sort_by_key(|item| item.row_indexes.start);
771
772 let first_row = items[0].row_indexes.start;
774
775 let first_row_items = &items[0..].split(|item| item.row_indexes.start != first_row).next().unwrap();
777
778 let row_has_baseline_item = first_row_items.iter().any(|item| item.align_self == AlignSelf::BASELINE);
780
781 let item = if row_has_baseline_item {
782 first_row_items.iter().find(|item| item.align_self == AlignSelf::BASELINE).unwrap()
783 } else {
784 &first_row_items[0]
785 };
786
787 item.y_position + item.baseline.unwrap_or(item.height)
788 };
789
790 #[cfg(feature = "content_size")]
793 let content_size = {
794 let mut content_size = item_content_size_contribution;
795 content_size.width += if direction.is_rtl() { padding.left } else { padding.right };
796 content_size.height += padding.bottom;
797 content_size.f32_max(absolute_content_size)
798 };
799 #[cfg(not(feature = "content_size"))]
800 let content_size = item_content_size_contribution;
801
802 LayoutOutput::from_sizes_and_baselines(
803 container_border_box,
804 content_size,
805 Point { x: None, y: Some(grid_container_baseline) },
806 )
807}
808
809fn reverse_non_gutter_tracks(tracks: &mut [GridTrack], track_counts: TrackCounts) {
811 if track_counts.explicit <= 1 {
814 const MIN_TRACK_VEC_LEN_TO_REVERSE_COLUMNS: usize = 5;
815 if tracks.len() < MIN_TRACK_VEC_LEN_TO_REVERSE_COLUMNS {
816 return;
817 }
818 let mut left = 1;
819 let mut right = tracks.len() - 2;
820 while left < right {
821 tracks.swap(left, right);
822 left += 2;
823 right = right.saturating_sub(2);
824 }
825 return;
826 }
827
828 let explicit_track_count = track_counts.explicit as usize;
829 if explicit_track_count < 2 {
830 return;
831 }
832
833 let mut left = track_counts.negative_implicit as usize;
834 let mut right = left + explicit_track_count - 1;
835 while left < right {
836 tracks.swap((2 * left) + 1, (2 * right) + 1);
837 left += 1;
838 right = right.saturating_sub(1);
839 }
840}
841
842fn rtl_column_occupancy_index_for_initialization(column_index: usize, track_counts: TrackCounts) -> usize {
844 if track_counts.explicit <= 1 {
845 return track_counts.len() - column_index - 1;
846 }
847
848 let explicit_start = track_counts.negative_implicit as usize;
849 let explicit_end = explicit_start + track_counts.explicit as usize;
850 if (explicit_start..explicit_end).contains(&column_index) {
851 explicit_start + (explicit_end - column_index - 1)
852 } else {
853 column_index
854 }
855}
856
857#[derive(Debug, Clone, PartialEq)]
859#[cfg(feature = "detailed_layout_info")]
860pub struct DetailedGridInfo {
861 pub rows: DetailedGridTracksInfo,
863 pub columns: DetailedGridTracksInfo,
865 pub items: Vec<DetailedGridItemsInfo>,
867}
868
869#[derive(Debug, Clone, PartialEq)]
871#[cfg(feature = "detailed_layout_info")]
872pub struct DetailedGridTracksInfo {
873 pub negative_implicit_tracks: u16,
875 pub explicit_tracks: u16,
877 pub positive_implicit_tracks: u16,
879
880 pub gutters: Vec<f32>,
882 pub sizes: Vec<f32>,
884}
885
886#[cfg(feature = "detailed_layout_info")]
887impl DetailedGridTracksInfo {
888 #[inline(always)]
890 fn grid_track_base_size_of_kind(grid_tracks: &[GridTrack], kind: GridTrackKind) -> Vec<f32> {
891 grid_tracks
892 .iter()
893 .filter_map(|track| match track.kind == kind {
894 true => Some(track.base_size),
895 false => None,
896 })
897 .collect()
898 }
899
900 fn gutters_from_grid_track_layout(grid_tracks: &[GridTrack]) -> Vec<f32> {
902 DetailedGridTracksInfo::grid_track_base_size_of_kind(grid_tracks, GridTrackKind::Gutter)
903 }
904
905 fn sizes_from_grid_track_layout(grid_tracks: &[GridTrack]) -> Vec<f32> {
907 DetailedGridTracksInfo::grid_track_base_size_of_kind(grid_tracks, GridTrackKind::Track)
908 }
909
910 fn from_grid_tracks_and_track_count(track_count: TrackCounts, grid_tracks: Vec<GridTrack>) -> Self {
912 DetailedGridTracksInfo {
913 negative_implicit_tracks: track_count.negative_implicit,
914 explicit_tracks: track_count.explicit,
915 positive_implicit_tracks: track_count.positive_implicit,
916 gutters: DetailedGridTracksInfo::gutters_from_grid_track_layout(&grid_tracks),
917 sizes: DetailedGridTracksInfo::sizes_from_grid_track_layout(&grid_tracks),
918 }
919 }
920}
921
922#[derive(Debug, Clone, PartialEq)]
927#[cfg(feature = "detailed_layout_info")]
928pub struct DetailedGridItemsInfo {
929 pub row_start: u16,
931 pub row_end: u16,
933 pub column_start: u16,
935 pub column_end: u16,
937}
938
939#[cfg(feature = "detailed_layout_info")]
941impl DetailedGridItemsInfo {
942 #[inline(always)]
944 fn from_grid_item(grid_item: &GridItem) -> Self {
945 #[inline(always)]
947 fn to_one_indexed_grid_line(grid_track_index: u16) -> u16 {
948 grid_track_index / 2 + 1
949 }
950
951 DetailedGridItemsInfo {
952 row_start: to_one_indexed_grid_line(grid_item.row_indexes.start),
953 row_end: to_one_indexed_grid_line(grid_item.row_indexes.end),
954 column_start: to_one_indexed_grid_line(grid_item.column_indexes.start),
955 column_end: to_one_indexed_grid_line(grid_item.column_indexes.end),
956 }
957 }
958}