Pārlūkot izejas kodu

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
Aliaksandr Kalenik 2 gadi atpakaļ
vecāks
revīzija
b2a04ff48a

+ 100 - 19
Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp

@@ -129,18 +129,22 @@ void TableFormattingContext::compute_table_measures()
         if (!computed_values.max_width().is_none())
         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));
             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_min_content_width = min_width + cell_intrinsic_offsets;
         auto cell_outer_max_content_width = max(max(width, min_width), max_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].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);
         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 const& table_box = context_box();
     auto& table_box_state = m_state.get_mutable(table_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);
         used_width = max(resolved_table_width, used_min_width);
         table_box_state.set_content_width(used_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;
         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)
 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.
     // 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 the width of the table among columns.
-    distribute_width_to_columns(extra_width);
+    distribute_width_to_columns();
 
 
     CSSPixels left_column_offset = 0;
     CSSPixels left_column_offset = 0;
     for (auto& column : m_columns) {
     for (auto& column : m_columns) {

+ 10 - 2
Userland/Libraries/LibWeb/Layout/TableFormattingContext.h

@@ -22,19 +22,27 @@ public:
 private:
 private:
     void calculate_row_column_grid(Box const&);
     void calculate_row_column_grid(Box const&);
     void compute_table_measures();
     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);
     void determine_intrisic_size_of_table_container(AvailableSpace const& available_space);
 
 
     CSSPixels m_automatic_content_height { 0 };
     CSSPixels m_automatic_content_height { 0 };
 
 
     Optional<AvailableSpace> m_available_space;
     Optional<AvailableSpace> m_available_space;
 
 
+    enum class ColumnType {
+        Percent,
+        Pixel,
+        Auto
+    };
+
     struct Column {
     struct Column {
+        ColumnType type { ColumnType::Auto };
         CSSPixels left_offset { 0 };
         CSSPixels left_offset { 0 };
         CSSPixels min_width { 0 };
         CSSPixels min_width { 0 };
         CSSPixels max_width { 0 };
         CSSPixels max_width { 0 };
         CSSPixels used_width { 0 };
         CSSPixels used_width { 0 };
+        float percentage_width { 0 };
     };
     };
 
 
     struct Row {
     struct Row {