From b2a04ff48a95f795647aacefe6d41230397f458f Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Thu, 5 Jan 2023 18:56:34 +0300 Subject: LibWeb: Consider percent and fixed widths in table column distribution Change column distribution to take in account is_length() and is_percentage() width values instead of treating all cells like they have auto width by implementing it in the way described in CSS Tables 3 spec: https://www.w3.org/TR/css-tables-3/#width-distribution-algorithm distribute_width_to_column() is structured to follow schema: w3.org/TR/css-tables-3/images/CSS-Tables-Column-Width-Assignment.svg --- .../LibWeb/Layout/TableFormattingContext.cpp | 119 +++++++++++++++++---- .../LibWeb/Layout/TableFormattingContext.h | 12 ++- 2 files changed, 110 insertions(+), 21 deletions(-) diff --git a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp index b7f21c8a01..96368c440c 100644 --- a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp @@ -129,18 +129,22 @@ void TableFormattingContext::compute_table_measures() if (!computed_values.max_width().is_none()) max_width = min(max_width, computed_values.max_width().resolved(cell.box, width_of_containing_block_as_length).to_px(cell.box)); + auto computed_width = computed_values.width(); + if (computed_width.is_percentage()) { + m_columns[cell.column_index].type = ColumnType::Percent; + m_columns[cell.column_index].percentage_width = max(m_columns[cell.column_index].percentage_width, computed_width.percentage().value()); + } else if (computed_width.is_length()) { + m_columns[cell.column_index].type = ColumnType::Pixel; + } + auto cell_outer_min_content_width = min_width + cell_intrinsic_offsets; auto cell_outer_max_content_width = max(max(width, min_width), max_width) + cell_intrinsic_offsets; m_columns[cell.column_index].min_width = max(m_columns[cell.column_index].min_width, cell_outer_min_content_width); m_columns[cell.column_index].max_width = max(m_columns[cell.column_index].max_width, cell_outer_max_content_width); } - - for (auto& column : m_columns) { - column.used_width = column.min_width; - } } -void TableFormattingContext::compute_table_width(CSSPixels& extra_width) +void TableFormattingContext::compute_table_width() { auto const& table_box = context_box(); auto& table_box_state = m_state.get_mutable(table_box); @@ -183,23 +187,101 @@ void TableFormattingContext::compute_table_width(CSSPixels& extra_width) used_width = max(resolved_table_width, used_min_width); table_box_state.set_content_width(used_width); } - - if (used_width > grid_min) { - extra_width = used_width - grid_min; - } } -void TableFormattingContext::distribute_width_to_columns(CSSPixels extra_width) +void TableFormattingContext::distribute_width_to_columns() { - CSSPixels grid_max = 0.0f; - for (auto& column : m_columns) - grid_max += column.max_width - column.min_width; + // Implements https://www.w3.org/TR/css-tables-3/#width-distribution-algorithm + + CSSPixels available_width = m_state.get(context_box()).content_width(); + + auto columns_total_used_width = [&]() { + CSSPixels total_used_width = 0; + for (auto& column : m_columns) { + total_used_width += column.used_width; + } + return total_used_width; + }; - if (grid_max == 0) + auto column_preferred_width = [&](Column& column) { + switch (column.type) { + case ColumnType::Percent: { + return max(column.min_width, column.percentage_width / 100 * available_width); + } + case ColumnType::Pixel: + case ColumnType::Auto: { + return column.max_width; + } + default: { + VERIFY_NOT_REACHED(); + } + } + }; + + auto expand_columns_to_fill_available_width = [&](ColumnType column_type) { + CSSPixels remaining_available_width = available_width; + CSSPixels total_preferred_width_increment = 0; + for (auto& column : m_columns) { + remaining_available_width -= column.used_width; + if (column.type == column_type) { + total_preferred_width_increment += column_preferred_width(column) - column.min_width; + } + } + + for (auto& column : m_columns) { + if (column.type == column_type) { + CSSPixels preferred_width_increment = column_preferred_width(column) - column.min_width; + column.used_width += preferred_width_increment * remaining_available_width / total_preferred_width_increment; + } + } + }; + + auto shrink_columns_to_fit_available_width = [&](ColumnType column_type) { + for (auto& column : m_columns) { + if (column.type == column_type) + column.used_width = column.min_width; + } + + expand_columns_to_fill_available_width(column_type); + }; + + for (auto& column : m_columns) { + column.used_width = column.min_width; + } + + for (auto& column : m_columns) { + if (column.type == ColumnType::Percent) { + column.used_width = max(column.min_width, column.percentage_width / 100 * available_width); + } + } + + if (columns_total_used_width() > available_width) { + shrink_columns_to_fit_available_width(ColumnType::Percent); return; + } - for (auto& column : m_columns) - column.used_width += ((column.max_width - column.min_width) / grid_max) * extra_width; + for (auto& column : m_columns) { + if (column.type == ColumnType::Pixel) { + column.used_width = column.max_width; + } + } + + if (columns_total_used_width() > available_width) { + shrink_columns_to_fit_available_width(ColumnType::Pixel); + return; + } + + if (columns_total_used_width() < available_width) { + expand_columns_to_fill_available_width(ColumnType::Auto); + } + + if (columns_total_used_width() < available_width) { + expand_columns_to_fill_available_width(ColumnType::Pixel); + } + + if (columns_total_used_width() < available_width) { + expand_columns_to_fill_available_width(ColumnType::Percent); + } } void TableFormattingContext::determine_intrisic_size_of_table_container(AvailableSpace const& available_space) @@ -242,11 +324,10 @@ void TableFormattingContext::run(Box const& box, LayoutMode, AvailableSpace cons } // Compute the width of the table. - CSSPixels extra_width = 0; - compute_table_width(extra_width); + compute_table_width(); // Distribute the width of the table among columns. - distribute_width_to_columns(extra_width); + distribute_width_to_columns(); CSSPixels left_column_offset = 0; for (auto& column : m_columns) { diff --git a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h index 2d4d25f34f..8f63e677cd 100644 --- a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h @@ -22,19 +22,27 @@ public: private: void calculate_row_column_grid(Box const&); void compute_table_measures(); - void compute_table_width(CSSPixels&); - void distribute_width_to_columns(CSSPixels extra_width); + void compute_table_width(); + void distribute_width_to_columns(); void determine_intrisic_size_of_table_container(AvailableSpace const& available_space); CSSPixels m_automatic_content_height { 0 }; Optional m_available_space; + enum class ColumnType { + Percent, + Pixel, + Auto + }; + struct Column { + ColumnType type { ColumnType::Auto }; CSSPixels left_offset { 0 }; CSSPixels min_width { 0 }; CSSPixels max_width { 0 }; CSSPixels used_width { 0 }; + float percentage_width { 0 }; }; struct Row { -- cgit v1.2.3