mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
layout: Paint collapsed table borders on their own (#35075)
We were previously splitting collapsed borders into two halves, and then paint each one as part of the corresponding cell. This looked wrong when the border style wasn't solid, or when a cell spanned multiple tracks and the border wasn't the same for all of them. Now the borders of a table wrapper, table grid or table cell aren't painted in collapsed borders mode. Instead, the resulting collapsed borders are painted on their own. Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
e43baed585
commit
d00d76c1e8
15 changed files with 208 additions and 124 deletions
|
@ -47,7 +47,9 @@ use crate::fragment_tree::{
|
|||
BackgroundMode, BoxFragment, Fragment, FragmentFlags, FragmentTree, SpecificLayoutInfo, Tag,
|
||||
TextFragment,
|
||||
};
|
||||
use crate::geom::{LengthPercentageOrAuto, PhysicalPoint, PhysicalRect};
|
||||
use crate::geom::{
|
||||
LengthPercentageOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize,
|
||||
};
|
||||
use crate::replaced::NaturalSizes;
|
||||
use crate::style_ext::{BorderStyleColor, ComputedValuesExt};
|
||||
|
||||
|
@ -650,9 +652,16 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
if section == StackingContextSection::Outline {
|
||||
self.build_outline(builder);
|
||||
return;
|
||||
match section {
|
||||
StackingContextSection::CollapsedTableBorders => {
|
||||
self.build_collapsed_table_borders(builder);
|
||||
return;
|
||||
},
|
||||
StackingContextSection::Outline => {
|
||||
self.build_outline(builder);
|
||||
return;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
self.build_hit_test(builder, self.border_rect);
|
||||
|
@ -893,7 +902,90 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn build_collapsed_table_borders(&mut self, builder: &mut DisplayListBuilder) {
|
||||
let Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(table_info)) =
|
||||
&self.fragment.detailed_layout_info
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let mut common =
|
||||
builder.common_properties(units::LayoutRect::default(), &self.fragment.style);
|
||||
let radius = wr::BorderRadius::default();
|
||||
let mut column_sum = Au::zero();
|
||||
for (x, column_size) in table_info.track_sizes.x.iter().enumerate() {
|
||||
let mut row_sum = Au::zero();
|
||||
for (y, row_size) in table_info.track_sizes.y.iter().enumerate() {
|
||||
let left_border = &table_info.collapsed_borders.x[x].list[y];
|
||||
let right_border = &table_info.collapsed_borders.x[x + 1].list[y];
|
||||
let top_border = &table_info.collapsed_borders.y[y].list[x];
|
||||
let bottom_border = &table_info.collapsed_borders.y[y + 1].list[x];
|
||||
let details = wr::BorderDetails::Normal(wr::NormalBorder {
|
||||
left: self.build_border_side(left_border.style_color.clone()),
|
||||
right: self.build_border_side(right_border.style_color.clone()),
|
||||
top: self.build_border_side(top_border.style_color.clone()),
|
||||
bottom: self.build_border_side(bottom_border.style_color.clone()),
|
||||
radius,
|
||||
do_aa: true,
|
||||
});
|
||||
let mut border_widths = PhysicalSides::new(
|
||||
top_border.width,
|
||||
right_border.width,
|
||||
bottom_border.width,
|
||||
left_border.width,
|
||||
);
|
||||
|
||||
let mut origin = PhysicalPoint::new(column_sum, row_sum);
|
||||
let mut size = PhysicalSize::new(*column_size, *row_size);
|
||||
if x == 0 {
|
||||
origin.x -= table_info.wrapper_border.left;
|
||||
size.width += table_info.wrapper_border.left;
|
||||
} else {
|
||||
border_widths.left = Au::zero();
|
||||
origin.x += left_border.width / 2;
|
||||
size.width -= left_border.width / 2;
|
||||
}
|
||||
if y == 0 {
|
||||
origin.y -= table_info.wrapper_border.top;
|
||||
size.height += table_info.wrapper_border.top;
|
||||
} else {
|
||||
border_widths.top = Au::zero();
|
||||
origin.y += top_border.width / 2;
|
||||
size.height -= top_border.width / 2;
|
||||
}
|
||||
if x + 1 == table_info.track_sizes.x.len() {
|
||||
size.width += table_info.wrapper_border.right;
|
||||
} else {
|
||||
size.width += border_widths.right / 2;
|
||||
}
|
||||
if y + 1 == table_info.track_sizes.y.len() {
|
||||
size.height += table_info.wrapper_border.bottom;
|
||||
} else {
|
||||
size.height += border_widths.bottom / 2;
|
||||
}
|
||||
let border_rect = PhysicalRect::new(origin, size)
|
||||
.translate(self.fragment.content_rect.origin.to_vector())
|
||||
.translate(self.containing_block.origin.to_vector())
|
||||
.to_webrender();
|
||||
common.clip_rect = border_rect;
|
||||
builder.wr().push_border(
|
||||
&common,
|
||||
border_rect,
|
||||
border_widths.to_webrender(),
|
||||
details,
|
||||
);
|
||||
row_sum += *row_size;
|
||||
}
|
||||
column_sum += *column_size;
|
||||
}
|
||||
}
|
||||
|
||||
fn build_border(&mut self, builder: &mut DisplayListBuilder) {
|
||||
if self.fragment.has_collapsed_borders() {
|
||||
// Avoid painting borders for tables and table parts in collapsed-borders mode,
|
||||
// since the resulting collapsed borders are painted on their own in a special way.
|
||||
return;
|
||||
}
|
||||
|
||||
let border = self.fragment.style.get_border();
|
||||
let border_widths = self.fragment.border.to_webrender();
|
||||
|
||||
|
@ -907,12 +999,7 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
let style_color = match &self.fragment.detailed_layout_info {
|
||||
Some(SpecificLayoutInfo::TableGridOrTableCell(table_info)) => {
|
||||
table_info.border_style_color.clone()
|
||||
},
|
||||
_ => BorderStyleColor::from_border(border),
|
||||
};
|
||||
let style_color = BorderStyleColor::from_border(border);
|
||||
let details = wr::BorderDetails::Normal(wr::NormalBorder {
|
||||
top: self.build_border_side(style_color.top),
|
||||
right: self.build_border_side(style_color.right),
|
||||
|
|
|
@ -34,7 +34,8 @@ use super::DisplayList;
|
|||
use crate::display_list::conversions::{FilterToWebRender, ToWebRender};
|
||||
use crate::display_list::{BuilderForBoxFragment, DisplayListBuilder};
|
||||
use crate::fragment_tree::{
|
||||
BoxFragment, ContainingBlockManager, Fragment, FragmentFlags, FragmentTree, PositioningFragment,
|
||||
BoxFragment, ContainingBlockManager, Fragment, FragmentFlags, FragmentTree,
|
||||
PositioningFragment, SpecificLayoutInfo,
|
||||
};
|
||||
use crate::geom::{AuOrAuto, PhysicalRect, PhysicalSides};
|
||||
use crate::style_ext::ComputedValuesExt;
|
||||
|
@ -87,6 +88,7 @@ pub(crate) type ContainingBlockInfo<'a> = ContainingBlockManager<'a, ContainingB
|
|||
pub(crate) enum StackingContextSection {
|
||||
OwnBackgroundsAndBorders,
|
||||
DescendantBackgroundsAndBorders,
|
||||
CollapsedTableBorders,
|
||||
Foreground,
|
||||
Outline,
|
||||
}
|
||||
|
@ -726,6 +728,16 @@ impl StackingContext {
|
|||
child.build_display_list(builder, &self.atomic_inline_stacking_containers);
|
||||
}
|
||||
|
||||
// Additional step 4.5: Collapsed table borders
|
||||
// This step isn't in the spec, but other browsers seem to paint them at this point.
|
||||
while contents.peek().is_some_and(|(_, child)| {
|
||||
child.section() == StackingContextSection::CollapsedTableBorders
|
||||
}) {
|
||||
let (i, child) = contents.next().unwrap();
|
||||
self.debug_push_print_item(DebugPrintField::Contents, i);
|
||||
child.build_display_list(builder, &self.atomic_inline_stacking_containers);
|
||||
}
|
||||
|
||||
// Step 5: Float stacking containers
|
||||
for (i, child) in self.float_stacking_containers.iter().enumerate() {
|
||||
self.debug_push_print_item(DebugPrintField::FloatStackingContainers, i);
|
||||
|
@ -1181,30 +1193,34 @@ impl BoxFragment {
|
|||
.for_absolute_and_fixed_descendants
|
||||
.scroll_node_id
|
||||
};
|
||||
stacking_context
|
||||
.contents
|
||||
.push(StackingContextContent::Fragment {
|
||||
scroll_node_id: new_scroll_node_id,
|
||||
reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
|
||||
clip_chain_id: new_clip_chain_id,
|
||||
section: self.get_stacking_context_section(),
|
||||
containing_block: containing_block.rect,
|
||||
fragment: fragment.clone(),
|
||||
is_hit_test_for_scrollable_overflow: false,
|
||||
});
|
||||
|
||||
if !self.style.get_outline().outline_width.is_zero() {
|
||||
let mut add_fragment = |section| {
|
||||
stacking_context
|
||||
.contents
|
||||
.push(StackingContextContent::Fragment {
|
||||
scroll_node_id: new_scroll_node_id,
|
||||
reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
|
||||
clip_chain_id: new_clip_chain_id,
|
||||
section: StackingContextSection::Outline,
|
||||
section,
|
||||
containing_block: containing_block.rect,
|
||||
fragment: fragment.clone(),
|
||||
is_hit_test_for_scrollable_overflow: false,
|
||||
});
|
||||
};
|
||||
|
||||
add_fragment(self.get_stacking_context_section());
|
||||
|
||||
if let Fragment::Box(box_fragment) = &fragment {
|
||||
if matches!(
|
||||
box_fragment.borrow().detailed_layout_info,
|
||||
Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(_))
|
||||
) {
|
||||
add_fragment(StackingContextSection::CollapsedTableBorders);
|
||||
}
|
||||
}
|
||||
|
||||
if !self.style.get_outline().outline_width.is_zero() {
|
||||
add_fragment(StackingContextSection::Outline);
|
||||
}
|
||||
|
||||
// We want to build the scroll frame after the background and border, because
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue