Skip to content

Commit

Permalink
Support Overflow::Scroll (#446)
Browse files Browse the repository at this point in the history
* Re-enable WIP overflow:scroll and overflow:auto support

* Remove WIP overflow:auto support

* Add tests for scrollbars taking up space in flexbox containers

* Implement scrollbar gutter reservation for flexbox

* Add main and cross getters to Point

* Don't floor node size by scrollbar gutter size

* Add test for scrollbars in flex nodes with explicit size

* Flex scrollbars constrained by available space

* Add test for justify_content: end with overflow:scroll

* Convert scrollbar_width from u8 to f32

* Implement scrollbar gutter reservation for the leaf algorithm

* Add tests for scrollbars taking up space in leaf nodes

* Implement scrollbar gutter reservation for CSS Grid

* Add tests for scrollbars taking up space in grid nodes

* Rename constants.padding_border to constants.content_box_inset

* Fix leftover reference to padding_border variable

* Rename grid tests to correctly name axes
  • Loading branch information
nicoburns committed Apr 23, 2023
1 parent aed6f69 commit daf4164
Show file tree
Hide file tree
Showing 68 changed files with 1,449 additions and 62 deletions.
8 changes: 3 additions & 5 deletions scripts/gentest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,14 +387,11 @@ fn generate_node(ident: &str, node: &Value) -> TokenStream {
}
let overflow_x = quote_overflow(&style["overflowX"]);
let overflow_y = quote_overflow(&style["overflowY"]);
let (overflow, _scrollbar_width) = if overflow_x.is_some() || overflow_y.is_some() {
let (overflow, scrollbar_width) = if overflow_x.is_some() || overflow_y.is_some() {
let overflow_x = overflow_x.unwrap_or(quote!(taffy::style::Overflow::Visible));
let overflow_y = overflow_y.unwrap_or(quote!(taffy::style::Overflow::Visible));
let overflow = quote!(overflow: taffy::geometry::Point { x: #overflow_x, y: #overflow_y },);
let scrollbar_width = quote_number_prop("scrollbar_width", style, |value: f32| {
let value = value as u8;
quote!(#value)
});
let scrollbar_width = quote_number_prop("scrollbar_width", style, |value: f32| quote!(#value));
(overflow, scrollbar_width)
} else {
(quote!(), quote!())
Expand Down Expand Up @@ -577,6 +574,7 @@ fn generate_node(ident: &str, node: &Value) -> TokenStream {
#flex_direction
#flex_wrap
#overflow
#scrollbar_width
#align_items
#align_self
#justify_items
Expand Down
86 changes: 51 additions & 35 deletions src/compute/flexbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,11 @@ struct AlgoConstants {
margin: Rect<f32>,
/// The border of this section
border: Rect<f32>,
/// The padding of this section
padding_border: Rect<f32>,
/// The space between the content box and the border box.
/// This consists of padding + border + scrollbar_gutter.
content_box_inset: Rect<f32>,
/// The size reserved for scrollbar gutters in each axis
scrollbar_gutter: Point<f32>,
/// The gap of this section
gap: Size<f32>,
/// The align_items property of this node
Expand Down Expand Up @@ -268,7 +271,7 @@ fn compute_preliminary(
// and then re-resolve gaps based on newly determined size
let original_gap = constants.gap;
if let Some(inner_main_size) = constants.node_inner_size.main(constants.dir) {
let outer_main_size = inner_main_size + constants.padding_border.main_axis_sum(constants.dir);
let outer_main_size = inner_main_size + constants.content_box_inset.main_axis_sum(constants.dir);
constants.inner_container_size.set_main(constants.dir, inner_main_size);
constants.container_size.set_main(constants.dir, outer_main_size);
} else {
Expand Down Expand Up @@ -427,10 +430,20 @@ fn compute_constants(
let align_content = style.align_content.unwrap_or(AlignContent::Stretch);
let justify_content = style.justify_content;

let padding_border = padding + border;
// Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`.
// However, the axis are switched (transposed) because a node that scrolls vertically needs
// *horizontal* space to be reserved for a scrollbar
let scrollbar_gutter = style.overflow.transpose().map(|overflow| match overflow {
Overflow::Scroll => style.scrollbar_width,
_ => 0.0,
});
// TODO: make side configurable based on the `direction` property
let mut content_box_inset = padding + border;
content_box_inset.right += scrollbar_gutter.x;
content_box_inset.bottom += scrollbar_gutter.y;

let node_outer_size = known_dimensions;
let node_inner_size = node_outer_size.maybe_sub(padding_border.sum_axes());
let node_inner_size = node_outer_size.maybe_sub(content_box_inset.sum_axes());
let gap = style.gap.resolve_or_zero(node_inner_size.or(Size::zero()));

let container_size = Size::zero();
Expand All @@ -447,7 +460,8 @@ fn compute_constants(
margin,
border,
gap,
padding_border,
content_box_inset,
scrollbar_gutter,
align_items,
align_content,
justify_content,
Expand Down Expand Up @@ -531,19 +545,19 @@ fn determine_available_space(
) -> Size<AvailableSpace> {
// Note: min/max/preferred size styles have already been applied to known_dimensions in the `compute` function above
let width = match known_dimensions.width {
Some(node_width) => AvailableSpace::Definite(node_width - constants.padding_border.horizontal_axis_sum()),
Some(node_width) => AvailableSpace::Definite(node_width - constants.content_box_inset.horizontal_axis_sum()),
None => outer_available_space
.width
.maybe_sub(constants.margin.horizontal_axis_sum())
.maybe_sub(constants.padding_border.horizontal_axis_sum()),
.maybe_sub(constants.content_box_inset.horizontal_axis_sum()),
};

let height = match known_dimensions.height {
Some(node_height) => AvailableSpace::Definite(node_height - constants.padding_border.vertical_axis_sum()),
Some(node_height) => AvailableSpace::Definite(node_height - constants.content_box_inset.vertical_axis_sum()),
None => outer_available_space
.height
.maybe_sub(constants.margin.vertical_axis_sum())
.maybe_sub(constants.padding_border.vertical_axis_sum()),
.maybe_sub(constants.content_box_inset.vertical_axis_sum()),
};

Size { width, height }
Expand Down Expand Up @@ -824,7 +838,7 @@ fn determine_container_main_size(
lines: &mut Vec<FlexLine<'_>>,
constants: &mut AlgoConstants,
) {
let main_padding_border = constants.padding_border.main_axis_sum(constants.dir);
let main_content_box_inset = constants.content_box_inset.main_axis_sum(constants.dir);

let outer_main_size: f32 = constants.node_outer_size.main(constants.dir).unwrap_or_else(|| {
match main_axis_available_space {
Expand All @@ -845,7 +859,7 @@ fn determine_container_main_size(
})
.max_by(|a, b| a.total_cmp(b))
.unwrap_or(0.0);
let size = longest_line_length + main_padding_border;
let size = longest_line_length + main_content_box_inset;
if lines.len() > 1 {
f32_max(size, main_axis_available_space)
} else {
Expand All @@ -869,7 +883,7 @@ fn determine_container_main_size(
})
.max_by(|a, b| a.total_cmp(b))
.unwrap_or(0.0);
longest_line_length + main_padding_border
longest_line_length + main_content_box_inset
}
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
// Define a base main_size variable. This is mutated once for iteration over the outer
Expand Down Expand Up @@ -938,12 +952,12 @@ fn determine_container_main_size(
// Ultimately, this was not found by reading the spec, but by trial and error fixing tests to align with Webkit/Firefox output.
// (see the `flex_basis_unconstraint_row` and `flex_basis_uncontraint_column` generated tests which demonstrate this)
if constants.is_row {
content_main_size.maybe_clamp(style_min, style_max).max(main_padding_border)
content_main_size.maybe_clamp(style_min, style_max).max(main_content_box_inset)
} else {
content_main_size
.max(item.flex_basis)
.maybe_clamp(style_min, style_max)
.max(main_padding_border)
.max(main_content_box_inset)
}
}
};
Expand Down Expand Up @@ -1004,17 +1018,17 @@ fn determine_container_main_size(
main_size = f32_max(main_size, item_main_size_sum + gap_sum)
}

main_size + main_padding_border
main_size + main_content_box_inset
}
}
});

let outer_main_size = outer_main_size
.maybe_clamp(constants.min_size.main(constants.dir), constants.max_size.main(constants.dir))
.max(main_padding_border);
.max(main_content_box_inset - constants.scrollbar_gutter.main(constants.dir));

// let outer_main_size = inner_main_size + constants.padding_border.main_axis_sum(constants.dir);
let inner_main_size = outer_main_size - constants.padding_border.main_axis_sum(constants.dir);
let inner_main_size = f32_max(outer_main_size - main_content_box_inset, 0.0);
constants.container_size.set_main(constants.dir, outer_main_size);
constants.inner_container_size.set_main(constants.dir, inner_main_size);
constants.node_inner_size.set_main(constants.dir, Some(inner_main_size));
Expand Down Expand Up @@ -1358,7 +1372,7 @@ fn calculate_cross_size(flex_lines: &mut [FlexLine], node_size: Size<Option<f32>
AlignContent::Stretch | AlignContent::SpaceEvenly | AlignContent::SpaceAround
))
{
let cross_axis_padding_border = constants.padding_border.cross_axis_sum(constants.dir);
let cross_axis_padding_border = constants.content_box_inset.cross_axis_sum(constants.dir);
let cross_min_size = constants.min_size.cross(constants.dir);
let cross_max_size = constants.max_size.cross(constants.dir);
flex_lines[0].cross_size = node_size
Expand Down Expand Up @@ -1410,7 +1424,7 @@ fn calculate_cross_size(flex_lines: &mut [FlexLine], node_size: Size<Option<f32>
#[inline]
fn handle_align_content_stretch(flex_lines: &mut [FlexLine], node_size: Size<Option<f32>>, constants: &AlgoConstants) {
if constants.align_content == AlignContent::Stretch {
let cross_axis_padding_border = constants.padding_border.cross_axis_sum(constants.dir);
let cross_axis_padding_border = constants.content_box_inset.cross_axis_sum(constants.dir);
let cross_min_size = constants.min_size.cross(constants.dir);
let cross_max_size = constants.max_size.cross(constants.dir);
let container_min_inner_cross = node_size
Expand Down Expand Up @@ -1668,15 +1682,16 @@ fn determine_container_cross_size(
let total_cross_axis_gap = sum_axis_gaps(constants.gap.cross(constants.dir), flex_lines.len());
let total_line_cross_size: f32 = flex_lines.iter().map(|line| line.cross_size).sum::<f32>();

let padding_border_sum = constants.padding_border.cross_axis_sum(constants.dir);
let padding_border_sum = constants.content_box_inset.cross_axis_sum(constants.dir);
let cross_scrollbar_gutter = constants.scrollbar_gutter.cross(constants.dir);
let min_cross_size = constants.min_size.cross(constants.dir);
let max_cross_size = constants.max_size.cross(constants.dir);
let outer_container_size = node_size
.cross(constants.dir)
.unwrap_or(total_line_cross_size + total_cross_axis_gap + padding_border_sum)
.maybe_clamp(min_cross_size, max_cross_size)
.max(padding_border_sum);
let inner_container_size = outer_container_size - padding_border_sum;
.max(padding_border_sum - cross_scrollbar_gutter);
let inner_container_size = f32_max(outer_container_size - padding_border_sum, 0.0);

constants.container_size.set_cross(constants.dir, outer_container_size);
constants.inner_container_size.set_cross(constants.dir, inner_container_size);
Expand Down Expand Up @@ -1817,7 +1832,7 @@ fn calculate_layout_line(
/// Do a final layout pass and collect the resulting layouts.
#[inline]
fn final_layout_pass(tree: &mut impl LayoutTree, node: NodeId, flex_lines: &mut [FlexLine], constants: &AlgoConstants) {
let mut total_offset_cross = constants.padding_border.cross_start(constants.dir);
let mut total_offset_cross = constants.content_box_inset.cross_start(constants.dir);

if constants.is_wrap_reverse {
for line in flex_lines.iter_mut().rev() {
Expand All @@ -1828,7 +1843,7 @@ fn final_layout_pass(tree: &mut impl LayoutTree, node: NodeId, flex_lines: &mut
&mut total_offset_cross,
constants.container_size,
constants.node_inner_size,
constants.padding_border,
constants.content_box_inset,
constants.dir,
);
}
Expand All @@ -1841,7 +1856,7 @@ fn final_layout_pass(tree: &mut impl LayoutTree, node: NodeId, flex_lines: &mut
&mut total_offset_cross,
constants.container_size,
constants.node_inner_size,
constants.padding_border,
constants.content_box_inset,
constants.dir,
);
}
Expand Down Expand Up @@ -1980,20 +1995,21 @@ fn perform_absolute_layout_on_absolute_children(tree: &mut impl LayoutTree, node
| (JustifyContent::Stretch, false)
| (JustifyContent::FlexStart, false)
| (JustifyContent::FlexEnd, true) => {
constants.padding_border.main_start(constants.dir) + resolved_margin.main_start(constants.dir)
constants.content_box_inset.main_start(constants.dir) + resolved_margin.main_start(constants.dir)
}
(JustifyContent::End, _)
| (JustifyContent::FlexEnd, false)
| (JustifyContent::FlexStart, true)
| (JustifyContent::Stretch, true) => {
constants.container_size.main(constants.dir)
- constants.padding_border.main_end(constants.dir)
- constants.content_box_inset.main_end(constants.dir)
- final_size.main(constants.dir)
- resolved_margin.main_end(constants.dir)
}
(JustifyContent::SpaceEvenly, _) | (JustifyContent::SpaceAround, _) | (JustifyContent::Center, _) => {
(constants.container_size.main(constants.dir) + constants.padding_border.main_start(constants.dir)
- constants.padding_border.main_end(constants.dir)
(constants.container_size.main(constants.dir)
+ constants.content_box_inset.main_start(constants.dir)
- constants.content_box_inset.main_end(constants.dir)
- final_size.main(constants.dir)
+ resolved_margin.main_start(constants.dir)
- resolved_margin.main_end(constants.dir))
Expand All @@ -2020,20 +2036,20 @@ fn perform_absolute_layout_on_absolute_children(tree: &mut impl LayoutTree, node
(AlignSelf::Start, _)
| (AlignSelf::Baseline | AlignSelf::Stretch | AlignSelf::FlexStart, false)
| (AlignSelf::FlexEnd, true) => {
constants.padding_border.cross_start(constants.dir) + resolved_margin.cross_start(constants.dir)
constants.content_box_inset.cross_start(constants.dir) + resolved_margin.cross_start(constants.dir)
}
(AlignSelf::End, _)
| (AlignSelf::Baseline | AlignSelf::Stretch | AlignSelf::FlexStart, true)
| (AlignSelf::FlexEnd, false) => {
constants.container_size.cross(constants.dir)
- constants.padding_border.cross_end(constants.dir)
- constants.content_box_inset.cross_end(constants.dir)
- final_size.cross(constants.dir)
- resolved_margin.cross_end(constants.dir)
}
(AlignSelf::Center, _) => {
(constants.container_size.cross(constants.dir)
+ constants.padding_border.cross_start(constants.dir)
- constants.padding_border.cross_end(constants.dir)
+ constants.content_box_inset.cross_start(constants.dir)
- constants.content_box_inset.cross_end(constants.dir)
- final_size.cross(constants.dir)
+ resolved_margin.cross_start(constants.dir)
- resolved_margin.cross_end(constants.dir))
Expand Down Expand Up @@ -2104,7 +2120,7 @@ mod tests {
let padding = style.padding.resolve_or_zero(parent_size);
let padding_border = padding + border;
assert_eq!(constants.border, border);
assert_eq!(constants.padding_border, padding_border);
assert_eq!(constants.content_box_inset, padding_border);

let inner_size = Size {
width: node_size.width.maybe_sub(padding_border.horizontal_axis_sum()),
Expand Down
Loading

0 comments on commit daf4164

Please sign in to comment.