mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
layout: Add *very* basic support for table layout (#31121)
* layout: Add *very* basic support for table layout This is the first step to proper table layout. It implements a naive layout algorithm, notably only taking into account the preferred widths of the first table row. Still, it causes some float tests to start passing, so turn on the `layout.tables.enabled` preference for those directories. Co-authored-by: Oriol Brufau <obrufau@igalia.com> * Address review comments * Fix a crash with rowspan=0 * Turn on pref and update results for `/css/css-tables` and `/css/CSS2/tables` --------- Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
3d520f2668
commit
fc31e69f79
115 changed files with 842 additions and 315 deletions
|
@ -7,6 +7,8 @@ use std::convert::{TryFrom, TryInto};
|
|||
|
||||
use log::warn;
|
||||
use script_layout_interface::wrapper_traits::ThreadSafeLayoutNode;
|
||||
use servo_arc::Arc;
|
||||
use style::properties::ComputedValues;
|
||||
use style::selector_parser::PseudoElement;
|
||||
use style::str::char_is_whitespace;
|
||||
use style::values::specified::TextDecorationLine;
|
||||
|
@ -20,13 +22,14 @@ use crate::formatting_contexts::{
|
|||
IndependentFormattingContext, NonReplacedFormattingContext,
|
||||
NonReplacedFormattingContextContents,
|
||||
};
|
||||
use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags, Tag};
|
||||
use crate::style_ext::{DisplayGeneratingBox, DisplayLayoutInternal};
|
||||
|
||||
/// A reference to a slot and its coordinates in the table
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct ResolvedSlotAndLocation<'a> {
|
||||
cell: &'a TableSlotCell,
|
||||
coords: TableSlotCoordinates,
|
||||
pub(super) struct ResolvedSlotAndLocation<'a> {
|
||||
pub cell: &'a TableSlotCell,
|
||||
pub coords: TableSlotCoordinates,
|
||||
}
|
||||
|
||||
impl<'a> ResolvedSlotAndLocation<'a> {
|
||||
|
@ -114,12 +117,15 @@ impl Table {
|
|||
|
||||
/// Push a new slot into the last row of this table.
|
||||
fn push_new_slot_to_last_row(&mut self, slot: TableSlot) {
|
||||
self.slots.last_mut().expect("Should have rows").push(slot)
|
||||
}
|
||||
let last_row = match self.slots.last_mut() {
|
||||
Some(row) => row,
|
||||
None => {
|
||||
unreachable!("Should have some rows before calling `push_new_slot_to_last_row`")
|
||||
},
|
||||
};
|
||||
|
||||
/// Convenience method for get() that returns a SlotAndLocation
|
||||
fn get_slot<'a>(&'a self, coords: TableSlotCoordinates) -> Option<&'a TableSlot> {
|
||||
self.slots.get(coords.y)?.get(coords.x)
|
||||
self.size.width = self.size.width.max(last_row.len() + 1);
|
||||
last_row.push(slot);
|
||||
}
|
||||
|
||||
/// Find [`ResolvedSlotAndLocation`] of all the slots that cover the slot at the given
|
||||
|
@ -127,7 +133,7 @@ impl Table {
|
|||
/// the target and returns a [`ResolvedSlotAndLocation`] for each of them. If there is
|
||||
/// no slot at the given coordinates or that slot is an empty space, an empty vector
|
||||
/// is returned.
|
||||
fn resolve_slot_at<'a>(
|
||||
pub(super) fn resolve_slot_at<'a>(
|
||||
&'a self,
|
||||
coords: TableSlotCoordinates,
|
||||
) -> Vec<ResolvedSlotAndLocation<'a>> {
|
||||
|
@ -191,7 +197,6 @@ impl TableSlot {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TableBuilder {
|
||||
/// The table that we are building.
|
||||
table: Table,
|
||||
|
@ -209,7 +214,22 @@ pub struct TableBuilder {
|
|||
}
|
||||
|
||||
impl TableBuilder {
|
||||
pub fn finish(self) -> Table {
|
||||
pub(super) fn new(style: Arc<ComputedValues>) -> Self {
|
||||
Self {
|
||||
table: Table::new(style),
|
||||
incoming_rowspans: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_for_tests() -> Self {
|
||||
Self::new(ComputedValues::initial_values().to_arc())
|
||||
}
|
||||
|
||||
pub fn finish(mut self) -> Table {
|
||||
// Make sure that every row has the same number of cells.
|
||||
for row in self.table.slots.iter_mut() {
|
||||
row.resize_with(self.table.size.width, || TableSlot::Empty);
|
||||
}
|
||||
self.table
|
||||
}
|
||||
|
||||
|
@ -227,6 +247,7 @@ impl TableBuilder {
|
|||
|
||||
pub fn start_row<'builder>(&'builder mut self) {
|
||||
self.table.slots.push(Vec::new());
|
||||
self.table.size.height += 1;
|
||||
self.create_slots_for_cells_above_with_rowspan(true);
|
||||
}
|
||||
|
||||
|
@ -385,7 +406,7 @@ where
|
|||
context,
|
||||
info,
|
||||
propagated_text_decoration_line,
|
||||
builder: Default::default(),
|
||||
builder: TableBuilder::new(info.style.clone()),
|
||||
current_anonymous_row_content: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -592,12 +613,22 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
let tag = Tag::new_pseudo(
|
||||
self.info.node.opaque(),
|
||||
Some(PseudoElement::ServoAnonymousTableCell),
|
||||
);
|
||||
let base_fragment_info = BaseFragmentInfo {
|
||||
tag,
|
||||
flags: FragmentFlags::empty(),
|
||||
};
|
||||
|
||||
let block_container = builder.finish();
|
||||
self.table_traversal.builder.add_cell(TableSlotCell {
|
||||
contents: BlockFormattingContext::from_block_container(block_container),
|
||||
colspan: 1,
|
||||
rowspan: 1,
|
||||
id: 0, // This is just an id used for testing purposes.
|
||||
style: anonymous_info.style,
|
||||
base_fragment_info,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -657,7 +688,8 @@ where
|
|||
contents,
|
||||
colspan,
|
||||
rowspan,
|
||||
id: 0, // This is just an id used for testing purposes.
|
||||
style: info.style.clone(),
|
||||
base_fragment_info: info.into(),
|
||||
});
|
||||
|
||||
// We are doing this until we have actually set a Box for this `BoxSlot`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue