4 #include "render_item.h"
6 void litehtml::flex_line::distribute_free_space(int container_main_size
)
8 // Determine the used flex factor. Sum the outer hypothetical main sizes of all items on the line.
9 // If the sum is less than the flex container’s inner main size, use the flex grow factor for the
10 // rest of this algorithm; otherwise, use the flex shrink factor.
11 int initial_free_space
= container_main_size
- base_size
;
13 int total_flex_factor
;
14 if(initial_free_space
< 0)
17 total_flex_factor
= total_shrink
;
18 // Flex values between 0 and 1 have a somewhat special behavior: when the sum of the flex values on the line
19 // is less than 1, they will take up less than 100% of the free space.
20 // https://www.w3.org/TR/css-flexbox-1/#valdef-flex-flex-grow
21 if(total_flex_factor
< 1000)
23 for(auto &item
: items
)
25 item
->main_size
+= initial_free_space
* item
->shrink
/ 1000;
32 total_flex_factor
= total_grow
;
33 // Flex values between 0 and 1 have a somewhat special behavior: when the sum of the flex values on the line
34 // is less than 1, they will take up less than 100% of the free space.
35 // https://www.w3.org/TR/css-flexbox-1/#valdef-flex-flex-grow
36 if(total_flex_factor
< 1000)
38 for(auto &item
: items
)
40 item
->main_size
+= initial_free_space
* item
->grow
/ 1000;
46 if(total_flex_factor
> 0)
48 bool processed
= true;
51 int sum_scaled_flex_shrink_factor
= 0;
52 int sum_flex_factors
= 0;
53 int remaining_free_space
= container_main_size
;
54 int total_not_frozen
= 0;
55 for (auto &item
: items
)
59 sum_scaled_flex_shrink_factor
+= item
->scaled_flex_shrink_factor
;
62 sum_flex_factors
+= item
->grow
;
65 sum_flex_factors
+= item
->shrink
;
67 remaining_free_space
-= item
->base_size
;
71 remaining_free_space
-= item
->main_size
;
74 // Check for flexible items. If all the flex items on the line are frozen, free space has
75 // been distributed; exit this loop.
76 if (!total_not_frozen
) break;
78 remaining_free_space
= abs(remaining_free_space
);
79 // c. Distribute free space proportional to the flex factors.
80 // If the remaining free space is zero
82 if (!remaining_free_space
)
87 int total_clamped
= 0;
88 for (auto &item
: items
)
94 // If using the flex shrink factor
95 // For every unfrozen item on the line, multiply its flex shrink factor by its
96 // inner flex base size, and note this as its scaled flex shrink factor. Find
97 // the ratio of the item’s scaled flex shrink factor to the sum of the scaled
98 // flex shrink factors of all unfrozen items on the line. Set the item’s target
99 // main size to its flex base size minus a fraction of the absolute value of the
100 // remaining free space proportional to the ratio.
101 int scaled_flex_shrink_factor
= item
->base_size
* item
->shrink
;
102 item
->main_size
= (int) ((float) item
->base_size
- (float) remaining_free_space
*
103 (float) scaled_flex_shrink_factor
/
104 (float) sum_scaled_flex_shrink_factor
);
106 // d. Fix min/max violations. Clamp each non-frozen item’s target main size by its used
107 // min and max main sizes and floor its content-box size at zero. If the item’s target
108 // main size was made smaller by this, it’s a max violation. If the item’s target main
109 // size was made larger by this, it’s a min violation.
110 if (item
->main_size
<= item
->min_size
)
113 item
->main_size
= item
->min_size
;
116 if(!item
->max_size
.is_default() && item
->main_size
>= item
->max_size
)
119 item
->main_size
= item
->max_size
;
124 // If using the flex grow factor
125 // Find the ratio of the item’s flex grow factor to the sum of the flex grow
126 // factors of all unfrozen items on the line. Set the item’s target main size to
127 // its flex base size plus a fraction of the remaining free space proportional
129 item
->main_size
= (int) ((float) item
->base_size
+
130 (float) remaining_free_space
* (float) item
->grow
/
131 (float) total_flex_factor
);
132 // d. Fix min/max violations. Clamp each non-frozen item’s target main size by its used
133 // min and max main sizes and floor its content-box size at zero. If the item’s target
134 // main size was made smaller by this, it’s a max violation. If the item’s target main
135 // size was made larger by this, it’s a min violation.
136 if (item
->main_size
>= container_main_size
)
139 item
->main_size
= container_main_size
;
142 if(!item
->max_size
.is_default() && item
->main_size
>= item
->max_size
)
145 item
->main_size
= item
->max_size
;
151 if (total_clamped
== 0) processed
= false;
154 // Distribute remaining after algorithm space
155 int sum_main_size
= 0;
156 for(auto &item
: items
)
158 sum_main_size
+= item
->main_size
;
160 int free_space
= container_main_size
- sum_main_size
;
163 for(auto &item
: items
)
165 if(free_space
== 0) break;
173 bool litehtml::flex_line::distribute_main_auto_margins(int free_main_size
)
175 if(free_main_size
> 0 && (num_auto_margin_main_start
|| num_auto_margin_main_end
))
177 int add
= (int) (free_main_size
/ (items
.size() * 2));
178 for (auto &item
: items
)
180 if(!item
->auto_margin_main_start
.is_default())
182 item
->auto_margin_main_start
= add
;
183 item
->main_size
+= add
;
185 free_main_size
-= add
;
187 if(!item
->auto_margin_main_end
.is_default())
189 item
->auto_margin_main_end
= add
;
190 item
->main_size
+= add
;
192 free_main_size
-= add
;
195 while (free_main_size
> 0)
197 for (auto &item
: items
)
199 if(!item
->auto_margin_main_start
.is_default())
201 item
->auto_margin_main_start
= item
->auto_margin_main_start
+ 1;
203 if(!free_main_size
) break;
205 if(!item
->auto_margin_main_end
.is_default())
207 item
->auto_margin_main_end
= item
->auto_margin_main_end
+ 1;
209 if(!free_main_size
) break;
218 void litehtml::flex_line::init(int container_main_size
, bool fit_container
, bool is_row_direction
,
219 const litehtml::containing_block_context
&self_size
,
220 litehtml::formatting_context
*fmt_ctx
)
224 first_baseline
.set(0, baseline::baseline_type_none
);
225 last_baseline
.set(0, baseline::baseline_type_none
);
229 distribute_free_space(container_main_size
);
234 def_value
<int> first_baseline_top
= 0;
235 def_value
<int> first_baseline_bottom
= 0;
236 def_value
<int> last_baseline_top
= 0;
237 def_value
<int> last_baseline_bottom
= 0;
238 int non_baseline_height
= 0;
240 // Calculate maximum cross size
241 def_value
<int> max_cross_size(0);
242 if(self_size
.height
.type
!= containing_block_context::cbc_value_type_auto
)
244 max_cross_size
= self_size
.height
;
246 if(self_size
.max_height
.type
!= containing_block_context::cbc_value_type_none
)
248 if(max_cross_size
.is_default())
250 max_cross_size
= self_size
.max_height
;
253 max_cross_size
= std::max((int) max_cross_size
, (int) self_size
.max_height
);
257 /// Render items into new size
258 /// Find line cross_size
259 /// Find line first/last baseline
260 for (auto &item
: items
)
264 self_size
.new_width(item
->main_size
- item
->el
->content_offset_width(), containing_block_context::size_mode_exact_width
), fmt_ctx
, false);
266 if((item
->align
& 0xFF) == flex_align_items_baseline
)
268 if(item
->align
& flex_align_items_last
)
270 last_baseline
.type(reverse_cross
? baseline::baseline_type_top
: baseline::baseline_type_bottom
);
272 int top
= -item
->el
->get_last_baseline();
273 int bottom
= top
+ item
->el
->height();
275 if(last_baseline_top
.is_default()) last_baseline_top
= top
;
276 else last_baseline_top
= std::min((int) last_baseline_top
, top
);
278 if(last_baseline_bottom
.is_default()) last_baseline_bottom
= bottom
;
279 else last_baseline_bottom
= std::max((int)last_baseline_bottom
, bottom
);
282 first_baseline
.type(reverse_cross
? baseline::baseline_type_bottom
: baseline::baseline_type_top
);
283 int top
= -item
->el
->get_first_baseline();
284 int bottom
= top
+ item
->el
->height();
286 if(first_baseline_top
.is_default()) first_baseline_top
= top
;
287 else first_baseline_top
= std::min((int) first_baseline_top
, top
);
289 if(first_baseline_bottom
.is_default()) first_baseline_bottom
= bottom
;
290 else first_baseline_bottom
= std::max((int) first_baseline_bottom
, bottom
);
294 non_baseline_height
= std::max(non_baseline_height
, item
->el
->height());
296 main_size
+= item
->el
->width();
299 cross_size
= std::max(first_baseline_bottom
- first_baseline_top
,last_baseline_bottom
- last_baseline_top
);
300 cross_size
= std::max(cross_size
, non_baseline_height
);
301 if(!max_cross_size
.is_default() && cross_size
> max_cross_size
)
303 cross_size
= max_cross_size
;
306 first_baseline
.calc(first_baseline_top
, first_baseline_bottom
);
307 last_baseline
.calc(last_baseline_top
, last_baseline_bottom
);
310 // Calculate maximum cross size
311 def_value
<int> max_cross_size(0);
312 if(self_size
.width
.type
!= containing_block_context::cbc_value_type_auto
)
314 max_cross_size
= self_size
.width
;
316 if(self_size
.max_width
.type
!= containing_block_context::cbc_value_type_none
)
318 if(max_cross_size
.is_default())
320 max_cross_size
= self_size
.max_width
;
323 max_cross_size
= std::max((int) max_cross_size
, (int) self_size
.max_width
);
327 for (auto &item
: items
)
329 int el_ret_width
= item
->el
->render(0,
331 self_size
, fmt_ctx
, false);
334 self_size
.new_width_height(el_ret_width
- item
->el
->content_offset_width(),
335 item
->main_size
- item
->el
->content_offset_height(),
336 containing_block_context::size_mode_exact_width
|
337 containing_block_context::size_mode_exact_height
),
339 main_size
+= item
->el
->height();
340 cross_size
= std::max(cross_size
, item
->el
->width());
342 if(!max_cross_size
.is_default() && cross_size
> max_cross_size
)
344 cross_size
= max_cross_size
;
349 int litehtml::flex_line::calculate_items_position(int container_main_size
,
350 flex_justify_content justify_content
,
351 bool is_row_direction
,
352 const containing_block_context
&self_size
,
353 formatting_context
*fmt_ctx
)
355 /// Distribute main axis free space for auto-margins
356 int free_main_size
= container_main_size
- main_size
;
357 distribute_main_auto_margins(free_main_size
);
358 free_main_size
= container_main_size
- main_size
;
360 /// Fix justify-content property
361 switch (justify_content
)
363 case flex_justify_content_left
:
364 case flex_justify_content_right
:
365 if(!is_row_direction
)
367 justify_content
= flex_justify_content_start
;
370 case flex_justify_content_space_between
:
371 // If the leftover free-space is negative or there is only a single flex item on the line, this
372 // value is identical to flex-start.
373 if(items
.size() == 1 || free_main_size
< 0) justify_content
= flex_justify_content_flex_start
;
375 case flex_justify_content_space_around
:
376 case flex_justify_content_space_evenly
:
377 // If the leftover free-space is negative or there is only a single flex item on the line, this
378 // value is identical to center
379 if(items
.size() == 1 || free_main_size
< 0) justify_content
= flex_justify_content_center
;
385 /// Distribute free main size using justify-content property
387 int add_before_item
= 0;
388 int add_after_item
= 0;
389 int item_remainder
= 0;
391 /// find initial main position and spaces between items
392 switch (justify_content
)
395 case flex_justify_content_right
:
396 main_pos
= free_main_size
;
398 case flex_justify_content_left
:
399 case flex_justify_content_start
:
402 case flex_justify_content_end
:
403 main_pos
= free_main_size
;
405 case flex_justify_content_flex_end
:
408 main_pos
= free_main_size
;
411 case flex_justify_content_center
:
412 main_pos
= free_main_size
/ 2;
414 case flex_justify_content_space_between
:
415 add_after_item
= free_main_size
/ ((int) items
.size() - 1);
416 item_remainder
= free_main_size
- (add_after_item
* ((int) items
.size() - 1));
418 case flex_justify_content_space_around
:
419 add_after_item
= add_before_item
= free_main_size
/ ((int) items
.size() * 2);
420 item_remainder
= free_main_size
- (add_after_item
* (int) items
.size() * 2);
422 case flex_justify_content_space_evenly
:
423 add_before_item
= free_main_size
/ ((int) items
.size() + 1);
424 item_remainder
= free_main_size
- add_before_item
* ((int) items
.size() + 1);
429 main_pos
= free_main_size
;
434 /// Place all items in main and cross positions
436 for(auto &item
: items
)
438 main_pos
+= add_before_item
;
439 if(add_before_item
> 0 && item_remainder
> 0)
444 item
->place(*this, main_pos
, self_size
, fmt_ctx
);
445 main_pos
+= item
->get_el_main_size() + add_after_item
;
446 if(add_after_item
> 0 && item_remainder
> 0)
451 height
= std::max(height
, item
->el
->bottom());