add support for Ayatana indicator to Notification plugin
[claws.git] / src / plugins / litehtml_viewer / litehtml / flex_line.cpp
blob5f966b3b155aafafabed2254a6196159cd4b524f
1 #include "html.h"
2 #include "flex_line.h"
3 #include "flex_item.h"
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;
12 bool grow;
13 int total_flex_factor;
14 if(initial_free_space < 0)
16 grow = false;
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;
27 return;
29 } else
31 grow = true;
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;
42 return;
46 if(total_flex_factor > 0)
48 bool processed = true;
49 while (processed)
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)
57 if (!item->frozen)
59 sum_scaled_flex_shrink_factor += item->scaled_flex_shrink_factor;
60 if(grow)
62 sum_flex_factors += item->grow;
63 } else
65 sum_flex_factors += item->shrink;
67 remaining_free_space -= item->base_size;
68 total_not_frozen++;
69 } else
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
81 // Do nothing.
82 if (!remaining_free_space)
84 processed = false;
85 } else
87 int total_clamped = 0;
88 for (auto &item: items)
90 if (!item->frozen)
92 if(!grow)
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)
112 total_clamped++;
113 item->main_size = item->min_size;
114 item->frozen = true;
116 if(!item->max_size.is_default() && item->main_size >= item->max_size)
118 total_clamped++;
119 item->main_size = item->max_size;
120 item->frozen = true;
122 } else
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
128 // to the ratio.
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)
138 total_clamped++;
139 item->main_size = container_main_size;
140 item->frozen = true;
142 if(!item->max_size.is_default() && item->main_size >= item->max_size)
144 total_clamped++;
145 item->main_size = item->max_size;
146 item->frozen = true;
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;
161 if(free_space > 0)
163 for(auto &item : items)
165 if(free_space == 0) break;
166 item->main_size++;
167 free_space--;
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;
184 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;
191 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;
202 free_main_size--;
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;
208 free_main_size--;
209 if(!free_main_size) break;
213 return true;
215 return false;
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)
222 cross_size = 0;
223 main_size = 0;
224 first_baseline.set(0, baseline::baseline_type_none);
225 last_baseline.set(0, baseline::baseline_type_none);
227 if(!fit_container)
229 distribute_free_space(container_main_size);
232 if(is_row_direction)
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;
251 } else
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)
262 item->el->render(0,
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);
280 } else
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);
292 } else
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);
308 } else
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;
321 } else
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);
332 item->el->render(0,
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),
338 fmt_ctx, false);
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;
369 break;
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;
374 break;
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;
380 break;
381 default:
382 break;
385 /// Distribute free main size using justify-content property
386 int main_pos = 0;
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;
397 break;
398 case flex_justify_content_left:
399 case flex_justify_content_start:
400 main_pos = 0;
401 break;
402 case flex_justify_content_end:
403 main_pos = free_main_size;
404 break;
405 case flex_justify_content_flex_end:
406 if(!reverse_main)
408 main_pos = free_main_size;
410 break;
411 case flex_justify_content_center:
412 main_pos = free_main_size / 2;
413 break;
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));
417 break;
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);
421 break;
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);
425 break;
426 default:
427 if(reverse_main)
429 main_pos = free_main_size;
431 break;
434 /// Place all items in main and cross positions
435 int height = 0;
436 for(auto &item : items)
438 main_pos += add_before_item;
439 if(add_before_item > 0 && item_remainder > 0)
441 main_pos++;
442 item_remainder--;
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)
448 main_pos++;
449 item_remainder--;
451 height = std::max(height, item->el->bottom());
453 return height;