diff --git a/include/litehtml/flex_item.h b/include/litehtml/flex_item.h
index 48582988..192b421a 100644
--- a/include/litehtml/flex_item.h
+++ b/include/litehtml/flex_item.h
@@ -8,6 +8,14 @@ namespace litehtml
{
class flex_line;
+ enum flex_clamp_state
+ {
+ flex_clamp_state_unclamped,
+ flex_clamp_state_inflexible,
+ flex_clamp_state_min_violation,
+ flex_clamp_state_max_violation
+ };
+
/**
* Base class for flex item
*/
@@ -15,20 +23,28 @@ namespace litehtml
{
public:
std::shared_ptr el;
+
+ // All sizes should be interpreted as outer/margin-box sizes.
pixel_t base_size;
pixel_t min_size;
def_value max_size;
- pixel_t main_size;
+ pixel_t main_size; // Holds the outer hypothetical main size before distribute_free_space, and the used outer main size after.
+
int grow;
int shrink;
pixel_t scaled_flex_shrink_factor;
+
bool frozen;
+ flex_clamp_state clamp_state;
+
int order;
int src_order;
+
def_value auto_margin_main_start;
def_value auto_margin_main_end;
bool auto_margin_cross_start;
bool auto_margin_cross_end;
+
flex_align_items align;
explicit flex_item(std::shared_ptr &_el) :
@@ -41,6 +57,7 @@ namespace litehtml
shrink(0),
scaled_flex_shrink_factor(0),
frozen(false),
+ clamp_state(flex_clamp_state_unclamped),
order(0),
src_order(0),
auto_margin_main_start(0),
diff --git a/include/litehtml/flex_line.h b/include/litehtml/flex_line.h
index 06cf1bf2..a5057aed 100644
--- a/include/litehtml/flex_line.h
+++ b/include/litehtml/flex_line.h
@@ -12,11 +12,9 @@ namespace litehtml
public:
std::list> items;
pixel_t cross_start; // for row direction: top. for column direction: left
- pixel_t main_size; // sum of all items main size
+ pixel_t main_size; // sum of all items main size, initially the sum of hypothetical main sizes
pixel_t cross_size; // sum of all items cross size
pixel_t base_size;
- int total_grow;
- int total_shrink;
int num_auto_margin_main_start; // number of items with auto margin left/top
int num_auto_margin_main_end; // number of items with auto margin right/bottom
baseline first_baseline;
@@ -29,8 +27,6 @@ namespace litehtml
main_size(0),
cross_size(0),
base_size(0),
- total_grow(0),
- total_shrink(0),
num_auto_margin_main_start(0),
num_auto_margin_main_end(0),
first_baseline(),
@@ -50,6 +46,9 @@ namespace litehtml
formatting_context *fmt_ctx);
protected:
void distribute_free_space(pixel_t container_main_size);
+ void distribute_free_space_grow(pixel_t container_main_size);
+ void distribute_free_space_shrink(pixel_t container_main_size);
+ bool fix_min_max_violations();
};
}
diff --git a/src/flex_item.cpp b/src/flex_item.cpp
index 8281b534..32be8475 100644
--- a/src/flex_item.cpp
+++ b/src/flex_item.cpp
@@ -28,8 +28,18 @@ void litehtml::flex_item::init(const litehtml::containing_block_context &self_si
{
align = el->css().get_flex_align_self();
}
- main_size = base_size;
- scaled_flex_shrink_factor = base_size * shrink;
+
+ if (base_size < min_size)
+ {
+ main_size = min_size;
+ } else if (!max_size.is_default() && base_size > max_size)
+ {
+ main_size = max_size;
+ } else
+ {
+ main_size = base_size;
+ }
+
frozen = false;
}
@@ -137,12 +147,12 @@ void litehtml::flex_item_row_direction::direction_specific_init(const litehtml::
} else
{
min_size = el->css().get_min_width().calc_percent(self_size.render_width) +
- el->content_offset_width();
+ el->render_offset_width();
}
if (!el->css().get_max_width().is_predefined())
{
max_size = el->css().get_max_width().calc_percent(self_size.render_width) +
- el->content_offset_width();
+ el->render_offset_width();
}
bool flex_basis_predefined = el->css().get_flex_basis().is_predefined();
int predef = flex_basis_auto;
@@ -199,9 +209,10 @@ void litehtml::flex_item_row_direction::direction_specific_init(const litehtml::
} else
{
base_size = el->css().get_flex_basis().calc_percent(self_size.render_width) +
- el->content_offset_width();
- base_size = std::max(base_size, min_size);
+ el->render_offset_width();
}
+
+ scaled_flex_shrink_factor = (base_size - el->render_offset_width()) * shrink;
}
void litehtml::flex_item_row_direction::apply_main_auto_margins()
@@ -320,12 +331,12 @@ void litehtml::flex_item_column_direction::direction_specific_init(const litehtm
} else
{
min_size = el->css().get_min_height().calc_percent(self_size.height) +
- el->content_offset_height();
+ el->render_offset_height();
}
if (!el->css().get_max_height().is_predefined())
{
max_size = el->css().get_max_height().calc_percent(self_size.height) +
- el->content_offset_width();
+ el->render_offset_height();
}
bool flex_basis_predefined = el->css().get_flex_basis().is_predefined();
@@ -351,7 +362,7 @@ void litehtml::flex_item_column_direction::direction_specific_init(const litehtm
{
case flex_basis_auto:
base_size = el->css().get_height().calc_percent(self_size.height) +
- el->content_offset_height();
+ el->render_offset_height();
break;
case flex_basis_max_content:
case flex_basis_fit_content:
@@ -371,17 +382,18 @@ void litehtml::flex_item_column_direction::direction_specific_init(const litehtm
if(self_size.height.type == containing_block_context::cbc_value_type_absolute)
{
base_size = el->css().get_flex_basis().calc_percent(self_size.height) +
- el->content_offset_height();
+ el->render_offset_height();
} else
{
base_size = 0;
}
} else
{
- base_size = (pixel_t) el->css().get_flex_basis().val() + el->content_offset_height();
+ base_size = (pixel_t) el->css().get_flex_basis().val() + el->render_offset_height();
}
- base_size = std::max(base_size, min_size);
}
+
+ scaled_flex_shrink_factor = (base_size - el->render_offset_height()) * shrink;
}
void litehtml::flex_item_column_direction::apply_main_auto_margins()
diff --git a/src/flex_line.cpp b/src/flex_line.cpp
index 4674623d..12b7866d 100644
--- a/src/flex_line.cpp
+++ b/src/flex_line.cpp
@@ -4,161 +4,266 @@
void litehtml::flex_line::distribute_free_space(pixel_t container_main_size)
{
- // Determine the used flex factor. Sum the outer hypothetical main sizes of all items on the line.
+ // 1 Determine the used flex factor. Sum the outer hypothetical main sizes of all items on the line.
// If the sum is less than the flex container’s inner main size, use the flex grow factor for the
// rest of this algorithm; otherwise, use the flex shrink factor.
- pixel_t initial_free_space = container_main_size - base_size;
- bool grow;
- int total_flex_factor;
- if(initial_free_space < 0)
+
+ if (main_size < container_main_size)
+ {
+ distribute_free_space_grow(container_main_size);
+ } else
+ {
+ distribute_free_space_shrink(container_main_size);
+ }
+}
+
+void litehtml::flex_line::distribute_free_space_grow(pixel_t container_main_size)
+{
+ pixel_t initial_free_space = container_main_size;
+
+ bool all_inflexible = true;
+
+ for (auto& item : items)
{
- grow = false;
- total_flex_factor = total_shrink;
- // Flex values between 0 and 1 have a somewhat special behavior: when the sum of the flex values on the line
- // is less than 1, they will take up less than 100% of the free space.
- // https://www.w3.org/TR/css-flexbox-1/#valdef-flex-flex-grow
- if(total_flex_factor < 1000)
+ // 2. Size inflexible items. Freeze, setting its target main size to its hypothetical main size
+ // any item that has a flex factor of zero
+ // if using the flex grow factor: any item that has a flex base size greater than its hypothetical main size
+
+ // 3. Calculate initial free space. Sum the outer sizes of all items on the line, and subtract this
+ // from the flex container’s inner main size. For frozen items, use their outer target main size; for
+ // other items, use their outer flex base size.
+
+ if (item->grow == 0 || item->base_size > item->main_size)
+ {
+ item->frozen = true;
+ item->clamp_state = flex_clamp_state_inflexible;
+ initial_free_space -= item->main_size;
+ } else
{
- for(auto &item : items)
+ initial_free_space -= item->base_size;
+ all_inflexible = false;
+ }
+ }
+
+ // 4. Loop:
+
+ // 4.a Check for flexible items. If all the flex items on the line are frozen, free space has been
+ // distributed; exit this loop.
+
+ if (all_inflexible) return;
+
+ while (true)
+ {
+ // 4.b Calculate the remaining free space as for initial free space, above. If the sum of the
+ // unfrozen flex items’ flex factors is less than one, multiply the initial free space by this sum.
+ // If the magnitude of this value is less than the magnitude of the remaining free space, use
+ // this as the remaining free space.
+
+ int sum_flex_grow_factor = 0;
+ pixel_t remaining_free_space = container_main_size;
+
+ for (auto& item : items)
+ {
+ if (item->frozen)
+ {
+ remaining_free_space -= item->main_size;
+ } else
{
- item->main_size += initial_free_space * item->shrink / 1000;
+ remaining_free_space -= item->base_size;
+ sum_flex_grow_factor += item->grow;
}
- return;
}
- } else
- {
- grow = true;
- total_flex_factor = total_grow;
- // Flex values between 0 and 1 have a somewhat special behavior: when the sum of the flex values on the line
- // is less than 1, they will take up less than 100% of the free space.
- // https://www.w3.org/TR/css-flexbox-1/#valdef-flex-flex-grow
- if(total_flex_factor < 1000)
+
+ if (sum_flex_grow_factor < 1000)
{
- for(auto &item : items)
+ pixel_t adjusted_free_space = initial_free_space * (pixel_t) sum_flex_grow_factor / (pixel_t) 1000;
+ if (adjusted_free_space < remaining_free_space)
{
- item->main_size += initial_free_space * item->grow / 1000;
+ remaining_free_space = adjusted_free_space;
}
- return;
}
- }
- if(total_flex_factor > 0)
- {
- bool processed = true;
- while (processed)
+ // 4.c Distribute free space proportional to the flex factors.
+
+ // If the remaining free space is zero
+ // Do nothing.
+
+ if (remaining_free_space != 0)
{
- pixel_t sum_scaled_flex_shrink_factor = 0;
- pixel_t remaining_free_space = container_main_size;
- int total_not_frozen = 0;
- for (auto &item: items)
+ for (auto& item: items)
{
if (!item->frozen)
{
- sum_scaled_flex_shrink_factor += item->scaled_flex_shrink_factor;
- remaining_free_space -= item->base_size;
- total_not_frozen++;
- } else
- {
- remaining_free_space -= item->main_size;
+ // If using the flex grow factor
+ // Find the ratio of the item’s flex grow factor to the sum of the flex grow factors of all
+ // unfrozen items on the line. Set the item’s target main size to its flex base size plus a
+ // fraction of the remaining free space proportional to the ratio.
+
+ item->main_size = item->base_size + remaining_free_space * (pixel_t) item->grow / (pixel_t) sum_flex_grow_factor;
}
}
- // Check for flexible items. If all the flex items on the line are frozen, free space has
- // been distributed; exit this loop.
- if (!total_not_frozen) break;
-
- remaining_free_space = abs(remaining_free_space);
- // c. Distribute free space proportional to the flex factors.
- // If the remaining free space is zero
- // Do nothing.
- if (remaining_free_space == 0)
+ }
+
+ if (fix_min_max_violations()) break;
+ }
+}
+
+void litehtml::flex_line::distribute_free_space_shrink(pixel_t container_main_size)
+{
+ pixel_t initial_free_space = container_main_size;
+
+ bool all_inflexible = true;
+
+ for (auto& item : items)
+ {
+ // 2. Size inflexible items. Freeze, setting its target main size to its hypothetical main size
+ // any item that has a flex factor of zero
+ // if using the flex shrink factor: any item that has a flex base size smaller than its hypothetical main size
+
+ // 3. Calculate initial free space. Sum the outer sizes of all items on the line, and subtract this
+ // from the flex container’s inner main size. For frozen items, use their outer target main size; for
+ // other items, use their outer flex base size.
+
+ if (item->shrink == 0 || item->base_size < item->main_size)
+ {
+ item->frozen = true;
+ item->clamp_state = flex_clamp_state_inflexible;
+ initial_free_space -= item->main_size;
+ } else
+ {
+ initial_free_space -= item->base_size;
+ all_inflexible = false;
+ }
+ }
+
+ // 4. Loop:
+
+ // 4.a Check for flexible items. If all the flex items on the line are frozen, free space has been
+ // distributed; exit this loop.
+
+ if (all_inflexible) return;
+
+ while (true)
+ {
+ // 4.b Calculate the remaining free space as for initial free space, above. If the sum of the
+ // unfrozen flex items’ flex factors is less than one, multiply the initial free space by this sum.
+ // If the magnitude of this value is less than the magnitude of the remaining free space, use
+ // this as the remaining free space.
+
+ int sum_flex_shrink_factor = 0;
+ pixel_t sum_scaled_flex_shrink_factor = 0;
+ pixel_t remaining_free_space = container_main_size;
+
+ for (auto& item : items)
+ {
+ if (item->frozen)
{
- processed = false;
+ remaining_free_space -= item->main_size;
} else
{
- int total_clamped = 0;
- for (auto &item: items)
+ remaining_free_space -= item->base_size;
+ sum_flex_shrink_factor += item->shrink;
+ sum_scaled_flex_shrink_factor += item->scaled_flex_shrink_factor;
+ }
+ }
+
+ if (sum_flex_shrink_factor < 1000)
+ {
+ pixel_t adjusted_free_space = initial_free_space * (pixel_t) sum_flex_shrink_factor / (pixel_t) 1000;
+ if (adjusted_free_space > remaining_free_space)
+ {
+ remaining_free_space = adjusted_free_space;
+ }
+ }
+
+ // 4.c Distribute free space proportional to the flex factors.
+
+ // If the remaining free space is zero
+ // Do nothing.
+
+ if (remaining_free_space != 0)
+ {
+ for (auto& item: items)
+ {
+ if (!item->frozen)
{
- if (!item->frozen)
- {
- if(!grow)
- {
- // If using the flex shrink factor
- // For every unfrozen item on the line, multiply its flex shrink factor by its
- // inner flex base size, and note this as its scaled flex shrink factor. Find
- // the ratio of the item’s scaled flex shrink factor to the sum of the scaled
- // flex shrink factors of all unfrozen items on the line. Set the item’s target
- // main size to its flex base size minus a fraction of the absolute value of the
- // remaining free space proportional to the ratio.
- pixel_t scaled_flex_shrink_factor = item->base_size * item->shrink;
- item->main_size = item->base_size - remaining_free_space * scaled_flex_shrink_factor / sum_scaled_flex_shrink_factor;
-
- // d. Fix min/max violations. Clamp each non-frozen item’s target main size by its used
- // min and max main sizes and floor its content-box size at zero. If the item’s target
- // main size was made smaller by this, it’s a max violation. If the item’s target main
- // size was made larger by this, it’s a min violation.
- if (item->main_size <= item->min_size)
- {
- total_clamped++;
- item->main_size = item->min_size;
- item->frozen = true;
- }
- if(!item->max_size.is_default() && item->main_size >= item->max_size)
- {
- total_clamped++;
- item->main_size = item->max_size;
- item->frozen = true;
- }
- } else
- {
- // If using the flex grow factor
- // Find the ratio of the item’s flex grow factor to the sum of the flex grow
- // factors of all unfrozen items on the line. Set the item’s target main size to
- // its flex base size plus a fraction of the remaining free space proportional
- // to the ratio.
- item->main_size = item->base_size + remaining_free_space * (pixel_t) item->grow / (pixel_t) total_flex_factor;
-
- // d. Fix min/max violations. Clamp each non-frozen item’s target main size by its used
- // min and max main sizes and floor its content-box size at zero. If the item’s target
- // main size was made smaller by this, it’s a max violation. If the item’s target main
- // size was made larger by this, it’s a min violation.
- if (item->main_size >= container_main_size)
- {
- total_clamped++;
- item->main_size = container_main_size;
- item->frozen = true;
- }
- if(!item->max_size.is_default() && item->main_size >= item->max_size)
- {
- total_clamped++;
- item->main_size = item->max_size;
- item->frozen = true;
- }
- }
- }
+ // If using the flex shrink factor
+ // For every unfrozen item on the line, multiply its flex shrink factor by its inner flex base
+ // size, and note this as its scaled flex shrink factor. Find the ratio of the item’s scaled
+ // flex shrink factor to the sum of the scaled flex shrink factors of all unfrozen items on
+ // the line. Set the item’s target main size to its flex base size minus a fraction of the
+ // absolute value of the remaining free space proportional to the ratio.
+
+ item->main_size = item->base_size + remaining_free_space * item->scaled_flex_shrink_factor / sum_scaled_flex_shrink_factor;
}
- if (total_clamped == 0) processed = false;
}
}
- // Distribute remaining after algorithm space
- pixel_t sum_main_size = 0;
- for(auto &item : items)
+
+ if (fix_min_max_violations()) break;
+ }
+}
+
+bool litehtml::flex_line::fix_min_max_violations()
+{
+ // 4.d Fix min/max violations. Clamp each non-frozen item’s target main size by its used min
+ // and max main sizes and floor its content-box size at zero. If the item’s target main size was
+ // made smaller by this, it’s a max violation. If the item’s target main size was made larger
+ // by this, it’s a min violation.
+
+ pixel_t total_violation = 0;
+
+ for (auto& item : items)
+ {
+ if (!item->frozen)
{
- sum_main_size += item->main_size;
+ if (item->main_size < item->min_size)
+ {
+ total_violation += item->min_size - item->main_size;
+ item->main_size = item->min_size;
+ item->clamp_state = flex_clamp_state_min_violation;
+ } else if (!item->max_size.is_default() && item->main_size > item->max_size)
+ {
+ total_violation += item->max_size - item->main_size;
+ item->main_size = item->max_size;
+ item->clamp_state = flex_clamp_state_max_violation;
+ }
}
+ }
- pixel_t free_space = container_main_size - sum_main_size;
+ // 4.e Freeze over-flexed items. The total violation is the sum of the adjustments from the
+ // previous step ∑(clamped size - unclamped size). If the total violation is:
+ // Zero: Freeze all items.
+ // Positive: Freeze all the items with min violations.
+ // Negative: Freeze all the items with max violations.
- pixel_t ditribute_step = 1;
- if(free_space > 0)
+ if (total_violation == 0)
+ {
+ return true;
+ }
+
+ bool all_frozen = true;
+
+ flex_clamp_state state_to_freeze =
+ total_violation > 0
+ ? flex_clamp_state_min_violation
+ : flex_clamp_state_max_violation;
+
+ for (auto& item : items)
+ {
+ if (!item->frozen)
{
- for(auto &item : items)
+ if (item->clamp_state == state_to_freeze)
+ {
+ item->frozen = true;
+ } else
{
- if(free_space < ditribute_step) break;
- item->main_size += ditribute_step;
- free_space -= ditribute_step;
+ all_frozen = false;
+ item->clamp_state = flex_clamp_state_unclamped;
}
}
}
+
+ return all_frozen;
}
bool litehtml::flex_line::distribute_main_auto_margins(pixel_t free_main_size)
@@ -212,16 +317,16 @@ void litehtml::flex_line::init(pixel_t container_main_size, bool fit_container,
const litehtml::containing_block_context &self_size,
litehtml::formatting_context *fmt_ctx)
{
- cross_size = 0;
- main_size = 0;
- first_baseline.set(0, baseline::baseline_type_none);
- last_baseline.set(0, baseline::baseline_type_none);
-
if(!fit_container)
{
distribute_free_space(container_main_size);
}
+ cross_size = 0;
+ main_size = 0;
+ first_baseline.set(0, baseline::baseline_type_none);
+ last_baseline.set(0, baseline::baseline_type_none);
+
if(is_row_direction)
{
def_value first_baseline_top = 0;
diff --git a/src/render_block.cpp b/src/render_block.cpp
index 9eba62ad..359c0832 100644
--- a/src/render_block.cpp
+++ b/src/render_block.cpp
@@ -279,10 +279,7 @@ litehtml::pixel_t litehtml::render_item_block::_render(pixel_t x, pixel_t y, con
{
m_pos.height = self_size.height;
}
- if (src_el()->css().get_box_sizing() == box_sizing_border_box)
- {
- m_pos.height -= box_sizing_height();
- }
+ m_pos.height -= box_sizing_height();
} else if (src_el()->is_block_formatting_context())
{
// add the floats' height to the block height
diff --git a/src/render_flex.cpp b/src/render_flex.cpp
index 29291208..9f1eb7fd 100644
--- a/src/render_flex.cpp
+++ b/src/render_flex.cpp
@@ -35,11 +35,7 @@ litehtml::pixel_t litehtml::render_item_flex::_render_content(pixel_t x, pixel_t
{
if(self_size.height.type != containing_block_context::cbc_value_type_auto)
{
- container_main_size = self_size.height;
- if (css().get_box_sizing() == box_sizing_border_box)
- {
- container_main_size -= box_sizing_height();
- }
+ container_main_size = self_size.height - box_sizing_height();
} else
{
// Direction columns, height is auto - always in single line
@@ -100,11 +96,7 @@ litehtml::pixel_t litehtml::render_item_flex::_render_content(pixel_t x, pixel_t
{
if (self_size.height.type != containing_block_context::cbc_value_type_auto)
{
- pixel_t height = self_size.height;
- if (src_el()->css().get_box_sizing() == box_sizing_border_box)
- {
- height -= box_sizing_height();
- }
+ pixel_t height = self_size.height - box_sizing_height();
free_cross_size = height - sum_cross_size;
}
} else
@@ -301,14 +293,13 @@ std::list litehtml::render_item_flex::get_lines(const liteh
// Add flex items to lines
for(auto& item : items)
{
- if(!line.items.empty() && !single_line && line.base_size + item->base_size > container_main_size)
+ if(!line.items.empty() && !single_line && line.main_size + item->main_size > container_main_size)
{
lines.emplace_back(line);
line = flex_line(reverse_main, reverse_cross);
}
line.base_size += item->base_size;
- line.total_grow += item->grow;
- line.total_shrink += item->shrink;
+ line.main_size += item->main_size;
if(!item->auto_margin_main_start.is_default()) line.num_auto_margin_main_start++;
if(!item->auto_margin_main_end.is_default()) line.num_auto_margin_main_end++;
line.items.push_back(item);