2 #include "html_tag.h"
\r
3 #include "document.h"
\r
4 #include "iterators.h"
\r
5 #include "stylesheet.h"
\r
9 #include "el_before_after.h"
\r
11 litehtml::html_tag::html_tag(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc)
\r
13 m_box_sizing = box_sizing_content_box;
\r
15 m_overflow = overflow_visible;
\r
17 m_text_align = text_align_left;
\r
18 m_el_position = element_position_static;
\r
19 m_display = display_inline;
\r
20 m_vertical_align = va_baseline;
\r
21 m_list_style_type = list_style_type_none;
\r
22 m_list_style_position = list_style_position_outside;
\r
23 m_float = float_none;
\r
24 m_clear = clear_none;
\r
27 m_white_space = white_space_normal;
\r
28 m_lh_predefined = false;
\r
30 m_visibility = visibility_visible;
\r
31 m_border_spacing_x = 0;
\r
32 m_border_spacing_y = 0;
\r
33 m_border_collapse = border_collapse_separate;
\r
36 litehtml::html_tag::~html_tag()
\r
41 bool litehtml::html_tag::appendChild(const element::ptr &el)
\r
45 el->parent(shared_from_this());
\r
46 m_children.push_back(el);
\r
52 bool litehtml::html_tag::removeChild(const element::ptr &el)
\r
54 if(el && el->parent() == shared_from_this())
\r
56 el->parent(nullptr);
\r
57 m_children.erase(std::remove(m_children.begin(), m_children.end(), el), m_children.end());
\r
63 void litehtml::html_tag::clearRecursive()
\r
65 for(auto& el : m_children)
\r
67 el->clearRecursive();
\r
68 el->parent(nullptr);
\r
74 const litehtml::tchar_t* litehtml::html_tag::get_tagName() const
\r
76 return m_tag.c_str();
\r
79 void litehtml::html_tag::set_attr( const tchar_t* name, const tchar_t* val )
\r
83 tstring s_val = name;
\r
84 std::locale lc = std::locale::global(std::locale::classic());
\r
85 for(size_t i = 0; i < s_val.length(); i++)
\r
87 s_val[i] = std::tolower(s_val[i], lc);
\r
89 m_attrs[s_val] = val;
\r
91 if( t_strcasecmp( name, _t("class") ) == 0 )
\r
93 m_class_values.resize( 0 );
\r
94 split_string( val, m_class_values, _t(" ") );
\r
99 const litehtml::tchar_t* litehtml::html_tag::get_attr( const tchar_t* name, const tchar_t* def )
\r
101 string_map::const_iterator attr = m_attrs.find(name);
\r
102 if(attr != m_attrs.end())
\r
104 return attr->second.c_str();
\r
109 litehtml::elements_vector litehtml::html_tag::select_all( const tstring& selector )
\r
111 css_selector sel(media_query_list::ptr(0));
\r
112 sel.parse(selector);
\r
114 return select_all(sel);
\r
117 litehtml::elements_vector litehtml::html_tag::select_all( const css_selector& selector )
\r
119 litehtml::elements_vector res;
\r
120 select_all(selector, res);
\r
124 void litehtml::html_tag::select_all(const css_selector& selector, elements_vector& res)
\r
126 if(select(selector))
\r
128 res.push_back(shared_from_this());
\r
131 for(auto& el : m_children)
\r
133 el->select_all(selector, res);
\r
138 litehtml::element::ptr litehtml::html_tag::select_one( const tstring& selector )
\r
140 css_selector sel(media_query_list::ptr(0));
\r
141 sel.parse(selector);
\r
143 return select_one(sel);
\r
146 litehtml::element::ptr litehtml::html_tag::select_one( const css_selector& selector )
\r
148 if(select(selector))
\r
150 return shared_from_this();
\r
153 for(auto& el : m_children)
\r
155 element::ptr res = el->select_one(selector);
\r
164 void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet )
\r
166 remove_before_after();
\r
168 for(const auto& sel : stylesheet.selectors())
\r
170 int apply = select(*sel, false);
\r
172 if(apply != select_no_match)
\r
174 used_selector::ptr us = std::unique_ptr<used_selector>(new used_selector(sel, false));
\r
176 if(sel->is_media_valid())
\r
178 if(apply & select_match_pseudo_class)
\r
180 if(select(*sel, true))
\r
182 if(apply & select_match_with_after)
\r
184 element::ptr el = get_element_after();
\r
187 el->add_style(*sel->m_style);
\r
189 } else if(apply & select_match_with_before)
\r
191 element::ptr el = get_element_before();
\r
194 el->add_style(*sel->m_style);
\r
199 add_style(*sel->m_style);
\r
203 } else if(apply & select_match_with_after)
\r
205 element::ptr el = get_element_after();
\r
208 el->add_style(*sel->m_style);
\r
210 } else if(apply & select_match_with_before)
\r
212 element::ptr el = get_element_before();
\r
215 el->add_style(*sel->m_style);
\r
219 add_style(*sel->m_style);
\r
223 m_used_styles.push_back(std::move(us));
\r
227 for(auto& el : m_children)
\r
229 if(el->get_display() != display_inline_text)
\r
231 el->apply_stylesheet(stylesheet);
\r
236 void litehtml::html_tag::get_content_size( size& sz, int max_width )
\r
239 if(m_display == display_block)
\r
241 sz.width = max_width;
\r
248 void litehtml::html_tag::draw( uint_ptr hdc, int x, int y, const position* clip )
\r
250 position pos = m_pos;
\r
254 draw_background(hdc, x, y, clip);
\r
256 if(m_display == display_list_item && m_list_style_type != list_style_type_none)
\r
258 if(m_overflow > overflow_visible)
\r
260 position border_box = pos;
\r
261 border_box += m_padding;
\r
262 border_box += m_borders;
\r
264 border_radiuses bdr_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);
\r
266 bdr_radius -= m_borders;
\r
267 bdr_radius -= m_padding;
\r
269 get_document()->container()->set_clip(pos, bdr_radius, true, true);
\r
272 draw_list_marker(hdc, pos);
\r
274 if(m_overflow > overflow_visible)
\r
276 get_document()->container()->del_clip();
\r
281 litehtml::uint_ptr litehtml::html_tag::get_font(font_metrics* fm)
\r
285 *fm = m_font_metrics;
\r
290 const litehtml::tchar_t* litehtml::html_tag::get_style_property( const tchar_t* name, bool inherited, const tchar_t* def /*= 0*/ )
\r
292 const tchar_t* ret = m_style.get_property(name);
\r
293 element::ptr el_parent = parent();
\r
296 if ( ( ret && !t_strcasecmp(ret, _t("inherit")) ) || (!ret && inherited) )
\r
298 ret = el_parent->get_style_property(name, inherited, def);
\r
310 void litehtml::html_tag::parse_styles(bool is_reparse)
\r
312 const tchar_t* style = get_attr(_t("style"));
\r
316 m_style.add(style, NULL);
\r
320 document::ptr doc = get_document();
\r
322 m_el_position = (element_position) value_index(get_style_property(_t("position"), false, _t("static")), element_position_strings, element_position_fixed);
\r
323 m_text_align = (text_align) value_index(get_style_property(_t("text-align"), true, _t("left")), text_align_strings, text_align_left);
\r
324 m_overflow = (overflow) value_index(get_style_property(_t("overflow"), false, _t("visible")), overflow_strings, overflow_visible);
\r
325 m_white_space = (white_space) value_index(get_style_property(_t("white-space"), true, _t("normal")), white_space_strings, white_space_normal);
\r
326 m_display = (style_display) value_index(get_style_property(_t("display"), false, _t("inline")), style_display_strings, display_inline);
\r
327 m_visibility = (visibility) value_index(get_style_property(_t("visibility"), true, _t("visible")), visibility_strings, visibility_visible);
\r
328 m_box_sizing = (box_sizing) value_index(get_style_property(_t("box-sizing"), false, _t("content-box")), box_sizing_strings, box_sizing_content_box);
\r
330 if(m_el_position != element_position_static)
\r
332 const tchar_t* val = get_style_property(_t("z-index"), false, 0);
\r
335 m_z_index = t_atoi(val);
\r
339 const tchar_t* va = get_style_property(_t("vertical-align"), true, _t("baseline"));
\r
340 m_vertical_align = (vertical_align) value_index(va, vertical_align_strings, va_baseline);
\r
342 const tchar_t* fl = get_style_property(_t("float"), false, _t("none"));
\r
343 m_float = (element_float) value_index(fl, element_float_strings, float_none);
\r
345 m_clear = (element_clear) value_index(get_style_property(_t("clear"), false, _t("none")), element_clear_strings, clear_none);
\r
347 if (m_float != float_none)
\r
349 // reset display in to block for floating elements
\r
350 if (m_display != display_none)
\r
352 m_display = display_block;
\r
355 else if (m_display == display_table ||
\r
356 m_display == display_table_caption ||
\r
357 m_display == display_table_cell ||
\r
358 m_display == display_table_column ||
\r
359 m_display == display_table_column_group ||
\r
360 m_display == display_table_footer_group ||
\r
361 m_display == display_table_header_group ||
\r
362 m_display == display_table_row ||
\r
363 m_display == display_table_row_group)
\r
365 doc->add_tabular(shared_from_this());
\r
367 // fix inline boxes with absolute/fixed positions
\r
368 else if (m_display != display_none && is_inline_box())
\r
370 if (m_el_position == element_position_absolute || m_el_position == element_position_fixed)
\r
372 m_display = display_block;
\r
376 m_css_text_indent.fromString( get_style_property(_t("text-indent"), true, _t("0")), _t("0"));
\r
378 m_css_width.fromString( get_style_property(_t("width"), false, _t("auto")), _t("auto"));
\r
379 m_css_height.fromString( get_style_property(_t("height"), false, _t("auto")), _t("auto"));
\r
381 doc->cvt_units(m_css_width, m_font_size);
\r
382 doc->cvt_units(m_css_height, m_font_size);
\r
384 m_css_min_width.fromString( get_style_property(_t("min-width"), false, _t("0")));
\r
385 m_css_min_height.fromString( get_style_property(_t("min-height"), false, _t("0")));
\r
387 m_css_max_width.fromString( get_style_property(_t("max-width"), false, _t("none")), _t("none"));
\r
388 m_css_max_height.fromString( get_style_property(_t("max-height"), false, _t("none")), _t("none"));
\r
390 doc->cvt_units(m_css_min_width, m_font_size);
\r
391 doc->cvt_units(m_css_min_height, m_font_size);
\r
393 m_css_offsets.left.fromString( get_style_property(_t("left"), false, _t("auto")), _t("auto"));
\r
394 m_css_offsets.right.fromString( get_style_property(_t("right"), false, _t("auto")), _t("auto"));
\r
395 m_css_offsets.top.fromString( get_style_property(_t("top"), false, _t("auto")), _t("auto"));
\r
396 m_css_offsets.bottom.fromString( get_style_property(_t("bottom"), false, _t("auto")), _t("auto"));
\r
398 doc->cvt_units(m_css_offsets.left, m_font_size);
\r
399 doc->cvt_units(m_css_offsets.right, m_font_size);
\r
400 doc->cvt_units(m_css_offsets.top, m_font_size);
\r
401 doc->cvt_units(m_css_offsets.bottom, m_font_size);
\r
403 m_css_margins.left.fromString( get_style_property(_t("margin-left"), false, _t("0")), _t("auto"));
\r
404 m_css_margins.right.fromString( get_style_property(_t("margin-right"), false, _t("0")), _t("auto"));
\r
405 m_css_margins.top.fromString( get_style_property(_t("margin-top"), false, _t("0")), _t("auto"));
\r
406 m_css_margins.bottom.fromString( get_style_property(_t("margin-bottom"), false, _t("0")), _t("auto"));
\r
408 m_css_padding.left.fromString( get_style_property(_t("padding-left"), false, _t("0")), _t(""));
\r
409 m_css_padding.right.fromString( get_style_property(_t("padding-right"), false, _t("0")), _t(""));
\r
410 m_css_padding.top.fromString( get_style_property(_t("padding-top"), false, _t("0")), _t(""));
\r
411 m_css_padding.bottom.fromString( get_style_property(_t("padding-bottom"), false, _t("0")), _t(""));
\r
413 m_css_borders.left.width.fromString( get_style_property(_t("border-left-width"), false, _t("medium")), border_width_strings);
\r
414 m_css_borders.right.width.fromString( get_style_property(_t("border-right-width"), false, _t("medium")), border_width_strings);
\r
415 m_css_borders.top.width.fromString( get_style_property(_t("border-top-width"), false, _t("medium")), border_width_strings);
\r
416 m_css_borders.bottom.width.fromString( get_style_property(_t("border-bottom-width"), false, _t("medium")), border_width_strings);
\r
418 m_css_borders.left.color = web_color::from_string(get_style_property(_t("border-left-color"), false, _t("")));
\r
419 m_css_borders.left.style = (border_style) value_index(get_style_property(_t("border-left-style"), false, _t("none")), border_style_strings, border_style_none);
\r
421 m_css_borders.right.color = web_color::from_string(get_style_property(_t("border-right-color"), false, _t("")));
\r
422 m_css_borders.right.style = (border_style) value_index(get_style_property(_t("border-right-style"), false, _t("none")), border_style_strings, border_style_none);
\r
424 m_css_borders.top.color = web_color::from_string(get_style_property(_t("border-top-color"), false, _t("")));
\r
425 m_css_borders.top.style = (border_style) value_index(get_style_property(_t("border-top-style"), false, _t("none")), border_style_strings, border_style_none);
\r
427 m_css_borders.bottom.color = web_color::from_string(get_style_property(_t("border-bottom-color"), false, _t("")));
\r
428 m_css_borders.bottom.style = (border_style) value_index(get_style_property(_t("border-bottom-style"), false, _t("none")), border_style_strings, border_style_none);
\r
430 m_css_borders.radius.top_left_x.fromString(get_style_property(_t("border-top-left-radius-x"), false, _t("0")));
\r
431 m_css_borders.radius.top_left_y.fromString(get_style_property(_t("border-top-left-radius-y"), false, _t("0")));
\r
433 m_css_borders.radius.top_right_x.fromString(get_style_property(_t("border-top-right-radius-x"), false, _t("0")));
\r
434 m_css_borders.radius.top_right_y.fromString(get_style_property(_t("border-top-right-radius-y"), false, _t("0")));
\r
436 m_css_borders.radius.bottom_right_x.fromString(get_style_property(_t("border-bottom-right-radius-x"), false, _t("0")));
\r
437 m_css_borders.radius.bottom_right_y.fromString(get_style_property(_t("border-bottom-right-radius-y"), false, _t("0")));
\r
439 m_css_borders.radius.bottom_left_x.fromString(get_style_property(_t("border-bottom-left-radius-x"), false, _t("0")));
\r
440 m_css_borders.radius.bottom_left_y.fromString(get_style_property(_t("border-bottom-left-radius-y"), false, _t("0")));
\r
442 doc->cvt_units(m_css_borders.radius.bottom_left_x, m_font_size);
\r
443 doc->cvt_units(m_css_borders.radius.bottom_left_y, m_font_size);
\r
444 doc->cvt_units(m_css_borders.radius.bottom_right_x, m_font_size);
\r
445 doc->cvt_units(m_css_borders.radius.bottom_right_y, m_font_size);
\r
446 doc->cvt_units(m_css_borders.radius.top_left_x, m_font_size);
\r
447 doc->cvt_units(m_css_borders.radius.top_left_y, m_font_size);
\r
448 doc->cvt_units(m_css_borders.radius.top_right_x, m_font_size);
\r
449 doc->cvt_units(m_css_borders.radius.top_right_y, m_font_size);
\r
451 doc->cvt_units(m_css_text_indent, m_font_size);
\r
453 m_margins.left = doc->cvt_units(m_css_margins.left, m_font_size);
\r
454 m_margins.right = doc->cvt_units(m_css_margins.right, m_font_size);
\r
455 m_margins.top = doc->cvt_units(m_css_margins.top, m_font_size);
\r
456 m_margins.bottom = doc->cvt_units(m_css_margins.bottom, m_font_size);
\r
458 m_padding.left = doc->cvt_units(m_css_padding.left, m_font_size);
\r
459 m_padding.right = doc->cvt_units(m_css_padding.right, m_font_size);
\r
460 m_padding.top = doc->cvt_units(m_css_padding.top, m_font_size);
\r
461 m_padding.bottom = doc->cvt_units(m_css_padding.bottom, m_font_size);
\r
463 m_borders.left = doc->cvt_units(m_css_borders.left.width, m_font_size);
\r
464 m_borders.right = doc->cvt_units(m_css_borders.right.width, m_font_size);
\r
465 m_borders.top = doc->cvt_units(m_css_borders.top.width, m_font_size);
\r
466 m_borders.bottom = doc->cvt_units(m_css_borders.bottom.width, m_font_size);
\r
468 css_length line_height;
\r
469 line_height.fromString(get_style_property(_t("line-height"), true, _t("normal")), _t("normal"));
\r
470 if(line_height.is_predefined())
\r
472 m_line_height = m_font_metrics.height;
\r
473 m_lh_predefined = true;
\r
474 } else if(line_height.units() == css_units_none)
\r
476 m_line_height = (int) (line_height.val() * m_font_size);
\r
477 m_lh_predefined = false;
\r
480 m_line_height = doc->cvt_units(line_height, m_font_size, m_font_size);
\r
481 m_lh_predefined = false;
\r
485 if(m_display == display_list_item)
\r
487 const tchar_t* list_type = get_style_property(_t("list-style-type"), true, _t("disc"));
\r
488 m_list_style_type = (list_style_type) value_index(list_type, list_style_type_strings, list_style_type_disc);
\r
490 const tchar_t* list_pos = get_style_property(_t("list-style-position"), true, _t("outside"));
\r
491 m_list_style_position = (list_style_position) value_index(list_pos, list_style_position_strings, list_style_position_outside);
\r
493 const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0);
\r
494 if(list_image && list_image[0])
\r
497 css::parse_css_url(list_image, url);
\r
499 const tchar_t* list_image_baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0);
\r
500 doc->container()->load_image(url.c_str(), list_image_baseurl, true);
\r
505 parse_background();
\r
509 for(auto& el : m_children)
\r
511 el->parse_styles();
\r
516 int litehtml::html_tag::render( int x, int y, int max_width, bool second_pass )
\r
518 if (m_display == display_table || m_display == display_inline_table)
\r
520 return render_table(x, y, max_width, second_pass);
\r
523 return render_box(x, y, max_width, second_pass);
\r
526 bool litehtml::html_tag::is_white_space() const
\r
531 int litehtml::html_tag::get_font_size() const
\r
533 return m_font_size;
\r
536 int litehtml::html_tag::get_base_line()
\r
543 if(!m_boxes.empty())
\r
545 bl = m_boxes.back()->baseline() + content_margins_bottom();
\r
550 void litehtml::html_tag::init()
\r
552 if (m_display == display_table || m_display == display_inline_table)
\r
560 m_grid = std::unique_ptr<table_grid>(new table_grid());
\r
563 go_inside_table table_selector;
\r
564 table_rows_selector row_selector;
\r
565 table_cells_selector cell_selector;
\r
567 elements_iterator row_iter(shared_from_this(), &table_selector, &row_selector);
\r
569 element::ptr row = row_iter.next(false);
\r
572 m_grid->begin_row(row);
\r
574 elements_iterator cell_iter(row, &table_selector, &cell_selector);
\r
575 element::ptr cell = cell_iter.next();
\r
578 m_grid->add_cell(cell);
\r
580 cell = cell_iter.next(false);
\r
582 row = row_iter.next(false);
\r
588 for (auto& el : m_children)
\r
594 int litehtml::html_tag::select(const css_selector& selector, bool apply_pseudo)
\r
596 int right_res = select(selector.m_right, apply_pseudo);
\r
597 if(right_res == select_no_match)
\r
599 return select_no_match;
\r
601 element::ptr el_parent = parent();
\r
602 if(selector.m_left)
\r
606 return select_no_match;
\r
608 switch(selector.m_combinator)
\r
610 case combinator_descendant:
\r
612 bool is_pseudo = false;
\r
613 element::ptr res = find_ancestor(*selector.m_left, apply_pseudo, &is_pseudo);
\r
616 return select_no_match;
\r
621 right_res |= select_match_pseudo_class;
\r
626 case combinator_child:
\r
628 int res = el_parent->select(*selector.m_left, apply_pseudo);
\r
629 if(res == select_no_match)
\r
631 return select_no_match;
\r
634 if(right_res != select_match_pseudo_class)
\r
641 case combinator_adjacent_sibling:
\r
643 bool is_pseudo = false;
\r
644 element::ptr res = el_parent->find_adjacent_sibling(shared_from_this(), *selector.m_left, apply_pseudo, &is_pseudo);
\r
647 return select_no_match;
\r
652 right_res |= select_match_pseudo_class;
\r
657 case combinator_general_sibling:
\r
659 bool is_pseudo = false;
\r
660 element::ptr res = el_parent->find_sibling(shared_from_this(), *selector.m_left, apply_pseudo, &is_pseudo);
\r
663 return select_no_match;
\r
668 right_res |= select_match_pseudo_class;
\r
674 right_res = select_no_match;
\r
680 int litehtml::html_tag::select(const css_element_selector& selector, bool apply_pseudo)
\r
682 if(!selector.m_tag.empty() && selector.m_tag != _t("*"))
\r
684 if(selector.m_tag != m_tag)
\r
686 return select_no_match;
\r
690 int res = select_match;
\r
691 element::ptr el_parent = parent();
\r
693 for(css_attribute_selector::vector::const_iterator i = selector.m_attrs.begin(); i != selector.m_attrs.end(); i++)
\r
695 const tchar_t* attr_value = get_attr(i->attribute.c_str());
\r
696 switch(i->condition)
\r
698 case select_exists:
\r
701 return select_no_match;
\r
707 return select_no_match;
\r
710 if(i->attribute == _t("class"))
\r
712 const string_vector & tokens1 = m_class_values;
\r
713 const string_vector & tokens2 = i->class_val;
\r
715 for(string_vector::const_iterator str1 = tokens2.begin(); str1 != tokens2.end() && found; str1++)
\r
718 for(string_vector::const_iterator str2 = tokens1.begin(); str2 != tokens1.end() && !f; str2++)
\r
720 if( !t_strcasecmp(str1->c_str(), str2->c_str()) )
\r
732 return select_no_match;
\r
736 if( t_strcasecmp(i->val.c_str(), attr_value) )
\r
738 return select_no_match;
\r
743 case select_contain_str:
\r
746 return select_no_match;
\r
747 } else if(!t_strstr(attr_value, i->val.c_str()))
\r
749 return select_no_match;
\r
752 case select_start_str:
\r
755 return select_no_match;
\r
756 } else if(t_strncmp(attr_value, i->val.c_str(), i->val.length()))
\r
758 return select_no_match;
\r
761 case select_end_str:
\r
764 return select_no_match;
\r
765 } else if(t_strncmp(attr_value, i->val.c_str(), i->val.length()))
\r
767 const tchar_t* s = attr_value + t_strlen(attr_value) - i->val.length() - 1;
\r
770 return select_no_match;
\r
774 return select_no_match;
\r
778 case select_pseudo_element:
\r
779 if(i->val == _t("after"))
\r
781 res |= select_match_with_after;
\r
782 } else if(i->val == _t("before"))
\r
784 res |= select_match_with_before;
\r
787 return select_no_match;
\r
790 case select_pseudo_class:
\r
793 if (!el_parent) return select_no_match;
\r
795 tstring selector_param;
\r
796 tstring selector_name;
\r
798 tstring::size_type begin = i->val.find_first_of(_t('('));
\r
799 tstring::size_type end = (begin == tstring::npos) ? tstring::npos : find_close_bracket(i->val, begin);
\r
800 if(begin != tstring::npos && end != tstring::npos)
\r
802 selector_param = i->val.substr(begin + 1, end - begin - 1);
\r
804 if(begin != tstring::npos)
\r
806 selector_name = i->val.substr(0, begin);
\r
807 litehtml::trim(selector_name);
\r
810 selector_name = i->val;
\r
813 int selector = value_index(selector_name.c_str(), pseudo_class_strings);
\r
817 case pseudo_class_only_child:
\r
818 if (!el_parent->is_only_child(shared_from_this(), false))
\r
820 return select_no_match;
\r
823 case pseudo_class_only_of_type:
\r
824 if (!el_parent->is_only_child(shared_from_this(), true))
\r
826 return select_no_match;
\r
829 case pseudo_class_first_child:
\r
830 if (!el_parent->is_nth_child(shared_from_this(), 0, 1, false))
\r
832 return select_no_match;
\r
835 case pseudo_class_first_of_type:
\r
836 if (!el_parent->is_nth_child(shared_from_this(), 0, 1, true))
\r
838 return select_no_match;
\r
841 case pseudo_class_last_child:
\r
842 if (!el_parent->is_nth_last_child(shared_from_this(), 0, 1, false))
\r
844 return select_no_match;
\r
847 case pseudo_class_last_of_type:
\r
848 if (!el_parent->is_nth_last_child(shared_from_this(), 0, 1, true))
\r
850 return select_no_match;
\r
853 case pseudo_class_nth_child:
\r
854 case pseudo_class_nth_of_type:
\r
855 case pseudo_class_nth_last_child:
\r
856 case pseudo_class_nth_last_of_type:
\r
858 if(selector_param.empty()) return select_no_match;
\r
863 parse_nth_child_params(selector_param, num, off);
\r
864 if(!num && !off) return select_no_match;
\r
867 case pseudo_class_nth_child:
\r
868 if (!el_parent->is_nth_child(shared_from_this(), num, off, false))
\r
870 return select_no_match;
\r
873 case pseudo_class_nth_of_type:
\r
874 if (!el_parent->is_nth_child(shared_from_this(), num, off, true))
\r
876 return select_no_match;
\r
879 case pseudo_class_nth_last_child:
\r
880 if (!el_parent->is_nth_last_child(shared_from_this(), num, off, false))
\r
882 return select_no_match;
\r
885 case pseudo_class_nth_last_of_type:
\r
886 if (!el_parent->is_nth_last_child(shared_from_this(), num, off, true))
\r
888 return select_no_match;
\r
895 case pseudo_class_not:
\r
897 css_element_selector sel;
\r
898 sel.parse(selector_param);
\r
899 if(select(sel, apply_pseudo))
\r
901 return select_no_match;
\r
905 case pseudo_class_lang:
\r
907 trim( selector_param );
\r
909 if( !get_document()->match_lang( selector_param ) )
\r
911 return select_no_match;
\r
916 if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), i->val) == m_pseudo_classes.end())
\r
918 return select_no_match;
\r
924 res |= select_match_pseudo_class;
\r
932 litehtml::element::ptr litehtml::html_tag::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo)
\r
934 element::ptr el_parent = parent();
\r
939 int res = el_parent->select(selector, apply_pseudo);
\r
940 if(res != select_no_match)
\r
944 if(res & select_match_pseudo_class)
\r
949 *is_pseudo = false;
\r
954 return el_parent->find_ancestor(selector, apply_pseudo, is_pseudo);
\r
957 int litehtml::html_tag::get_floats_height(element_float el_float) const
\r
959 if(is_floats_holder())
\r
963 bool process = false;
\r
965 for(const auto& fb : m_floats_left)
\r
974 if (fb.clear_floats == clear_left || fb.clear_floats == clear_both)
\r
980 if (fb.clear_floats == clear_right || fb.clear_floats == clear_both)
\r
988 if(el_float == float_none)
\r
990 h = std::max(h, fb.pos.bottom());
\r
993 h = std::max(h, fb.pos.top());
\r
999 for(const auto fb : m_floats_right)
\r
1008 if (fb.clear_floats == clear_left || fb.clear_floats == clear_both)
\r
1014 if (fb.clear_floats == clear_right || fb.clear_floats == clear_both)
\r
1022 if(el_float == float_none)
\r
1024 h = std::max(h, fb.pos.bottom());
\r
1027 h = std::max(h, fb.pos.top());
\r
1034 element::ptr el_parent = parent();
\r
1037 int h = el_parent->get_floats_height(el_float);
\r
1038 return h - m_pos.y;
\r
1043 int litehtml::html_tag::get_left_floats_height() const
\r
1045 if(is_floats_holder())
\r
1048 if(!m_floats_left.empty())
\r
1050 for (const auto& fb : m_floats_left)
\r
1052 h = std::max(h, fb.pos.bottom());
\r
1057 element::ptr el_parent = parent();
\r
1060 int h = el_parent->get_left_floats_height();
\r
1061 return h - m_pos.y;
\r
1066 int litehtml::html_tag::get_right_floats_height() const
\r
1068 if(is_floats_holder())
\r
1071 if(!m_floats_right.empty())
\r
1073 for(const auto& fb : m_floats_right)
\r
1075 h = std::max(h, fb.pos.bottom());
\r
1080 element::ptr el_parent = parent();
\r
1083 int h = el_parent->get_right_floats_height();
\r
1084 return h - m_pos.y;
\r
1089 int litehtml::html_tag::get_line_left( int y )
\r
1091 if(is_floats_holder())
\r
1093 if(m_cahe_line_left.is_valid && m_cahe_line_left.hash == y)
\r
1095 return m_cahe_line_left.val;
\r
1099 for(const auto& fb : m_floats_left)
\r
1101 if (y >= fb.pos.top() && y < fb.pos.bottom())
\r
1103 w = std::max(w, fb.pos.right());
\r
1104 if (w < fb.pos.right())
\r
1110 m_cahe_line_left.set_value(y, w);
\r
1113 element::ptr el_parent = parent();
\r
1116 int w = el_parent->get_line_left(y + m_pos.y);
\r
1121 return w - (w ? m_pos.x : 0);
\r
1126 int litehtml::html_tag::get_line_right( int y, int def_right )
\r
1128 if(is_floats_holder())
\r
1130 if(m_cahe_line_right.is_valid && m_cahe_line_right.hash == y)
\r
1132 if(m_cahe_line_right.is_default)
\r
1137 return std::min(m_cahe_line_right.val, def_right);
\r
1141 int w = def_right;
\r
1142 m_cahe_line_right.is_default = true;
\r
1143 for(const auto& fb : m_floats_right)
\r
1145 if(y >= fb.pos.top() && y < fb.pos.bottom())
\r
1147 w = std::min(w, fb.pos.left());
\r
1148 m_cahe_line_right.is_default = false;
\r
1149 if(w > fb.pos.left())
\r
1155 m_cahe_line_right.set_value(y, w);
\r
1158 element::ptr el_parent = parent();
\r
1161 int w = el_parent->get_line_right(y + m_pos.y, def_right + m_pos.x);
\r
1162 return w - m_pos.x;
\r
1168 void litehtml::html_tag::get_line_left_right( int y, int def_right, int& ln_left, int& ln_right )
\r
1170 if(is_floats_holder())
\r
1172 ln_left = get_line_left(y);
\r
1173 ln_right = get_line_right(y, def_right);
\r
1176 element::ptr el_parent = parent();
\r
1179 el_parent->get_line_left_right(y + m_pos.y, def_right + m_pos.x, ln_left, ln_right);
\r
1181 ln_right -= m_pos.x;
\r
1182 ln_left -= m_pos.x;
\r
1191 int litehtml::html_tag::fix_line_width( int max_width, element_float flt )
\r
1193 int ret_width = 0;
\r
1194 if(!m_boxes.empty())
\r
1196 elements_vector els;
\r
1197 m_boxes.back()->get_elements(els);
\r
1198 bool was_cleared = false;
\r
1199 if(!els.empty() && els.front()->get_clear() != clear_none)
\r
1201 if(els.front()->get_clear() == clear_both)
\r
1203 was_cleared = true;
\r
1206 if( (flt == float_left && els.front()->get_clear() == clear_left) ||
\r
1207 (flt == float_right && els.front()->get_clear() == clear_right) )
\r
1209 was_cleared = true;
\r
1216 m_boxes.pop_back();
\r
1218 for(elements_vector::iterator i = els.begin(); i != els.end(); i++)
\r
1220 int rw = place_element((*i), max_width);
\r
1221 if(rw > ret_width)
\r
1229 if(m_boxes.back()->get_type() == box_line)
\r
1231 line_top = m_boxes.back()->top();
\r
1234 line_top = m_boxes.back()->bottom();
\r
1237 int line_left = 0;
\r
1238 int line_right = max_width;
\r
1239 get_line_left_right(line_top, max_width, line_left, line_right);
\r
1241 if(m_boxes.back()->get_type() == box_line)
\r
1243 if(m_boxes.size() == 1 && m_list_style_type != list_style_type_none && m_list_style_position == list_style_position_inside)
\r
1245 int sz_font = get_font_size();
\r
1246 line_left += sz_font;
\r
1249 if(m_css_text_indent.val() != 0)
\r
1251 bool line_box_found = false;
\r
1252 for(box::vector::iterator iter = m_boxes.begin(); iter < m_boxes.end(); iter++)
\r
1254 if((*iter)->get_type() == box_line)
\r
1256 line_box_found = true;
\r
1260 if(!line_box_found)
\r
1262 line_left += m_css_text_indent.calc_percent(max_width);
\r
1268 elements_vector els;
\r
1269 m_boxes.back()->new_width(line_left, line_right, els);
\r
1270 for(auto& el : els)
\r
1272 int rw = place_element(el, max_width);
\r
1273 if(rw > ret_width)
\r
1284 void litehtml::html_tag::add_float(const element::ptr &el, int x, int y)
\r
1286 if(is_floats_holder())
\r
1289 fb.pos.x = el->left() + x;
\r
1290 fb.pos.y = el->top() + y;
\r
1291 fb.pos.width = el->width();
\r
1292 fb.pos.height = el->height();
\r
1293 fb.float_side = el->get_float();
\r
1294 fb.clear_floats = el->get_clear();
\r
1297 if(fb.float_side == float_left)
\r
1299 if(m_floats_left.empty())
\r
1301 m_floats_left.push_back(fb);
\r
1304 bool inserted = false;
\r
1305 for(floated_box::vector::iterator i = m_floats_left.begin(); i != m_floats_left.end(); i++)
\r
1307 if(fb.pos.right() > i->pos.right())
\r
1309 m_floats_left.insert(i, std::move(fb));
\r
1316 m_floats_left.push_back(std::move(fb));
\r
1319 m_cahe_line_left.invalidate();
\r
1320 } else if(fb.float_side == float_right)
\r
1322 if(m_floats_right.empty())
\r
1324 m_floats_right.push_back(std::move(fb));
\r
1327 bool inserted = false;
\r
1328 for(floated_box::vector::iterator i = m_floats_right.begin(); i != m_floats_right.end(); i++)
\r
1330 if(fb.pos.left() < i->pos.left())
\r
1332 m_floats_right.insert(i, std::move(fb));
\r
1339 m_floats_right.push_back(fb);
\r
1342 m_cahe_line_right.invalidate();
\r
1346 element::ptr el_parent = parent();
\r
1349 el_parent->add_float(el, x + m_pos.x, y + m_pos.y);
\r
1354 int litehtml::html_tag::find_next_line_top( int top, int width, int def_right )
\r
1356 if(is_floats_holder())
\r
1358 int new_top = top;
\r
1359 int_vector points;
\r
1361 for(const auto& fb : m_floats_left)
\r
1363 if(fb.pos.top() >= top)
\r
1365 if(find(points.begin(), points.end(), fb.pos.top()) == points.end())
\r
1367 points.push_back(fb.pos.top());
\r
1370 if (fb.pos.bottom() >= top)
\r
1372 if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end())
\r
1374 points.push_back(fb.pos.bottom());
\r
1379 for (const auto& fb : m_floats_right)
\r
1381 if (fb.pos.top() >= top)
\r
1383 if (find(points.begin(), points.end(), fb.pos.top()) == points.end())
\r
1385 points.push_back(fb.pos.top());
\r
1388 if (fb.pos.bottom() >= top)
\r
1390 if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end())
\r
1392 points.push_back(fb.pos.bottom());
\r
1397 if(!points.empty())
\r
1399 sort(points.begin(), points.end(), std::less<int>( ));
\r
1400 new_top = points.back();
\r
1402 for(auto pt : points)
\r
1405 int pos_right = def_right;
\r
1406 get_line_left_right(pt, def_right, pos_left, pos_right);
\r
1408 if(pos_right - pos_left >= width)
\r
1417 element::ptr el_parent = parent();
\r
1420 int new_top = el_parent->find_next_line_top(top + m_pos.y, width, def_right + m_pos.x);
\r
1421 return new_top - m_pos.y;
\r
1426 void litehtml::html_tag::parse_background()
\r
1428 // parse background-color
\r
1429 m_bg.m_color = get_color(_t("background-color"), false, web_color(0, 0, 0, 0));
\r
1431 // parse background-position
\r
1432 const tchar_t* str = get_style_property(_t("background-position"), false, _t("0% 0%"));
\r
1435 string_vector res;
\r
1436 split_string(str, res, _t(" \t"));
\r
1437 if(res.size() > 0)
\r
1439 if(res.size() == 1)
\r
1441 if( value_in_list(res[0].c_str(), _t("left;right;center")) )
\r
1443 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
\r
1444 m_bg.m_position.y.set_value(50, css_units_percentage);
\r
1445 } else if( value_in_list(res[0].c_str(), _t("top;bottom;center")) )
\r
1447 m_bg.m_position.y.fromString(res[0], _t("top;bottom;center"));
\r
1448 m_bg.m_position.x.set_value(50, css_units_percentage);
\r
1451 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
\r
1452 m_bg.m_position.y.set_value(50, css_units_percentage);
\r
1456 if(value_in_list(res[0].c_str(), _t("left;right")))
\r
1458 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
\r
1459 m_bg.m_position.y.fromString(res[1], _t("top;bottom;center"));
\r
1460 } else if(value_in_list(res[0].c_str(), _t("top;bottom")))
\r
1462 m_bg.m_position.x.fromString(res[1], _t("left;right;center"));
\r
1463 m_bg.m_position.y.fromString(res[0], _t("top;bottom;center"));
\r
1464 } else if(value_in_list(res[1].c_str(), _t("left;right")))
\r
1466 m_bg.m_position.x.fromString(res[1], _t("left;right;center"));
\r
1467 m_bg.m_position.y.fromString(res[0], _t("top;bottom;center"));
\r
1468 }else if(value_in_list(res[1].c_str(), _t("top;bottom")))
\r
1470 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
\r
1471 m_bg.m_position.y.fromString(res[1], _t("top;bottom;center"));
\r
1474 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
\r
1475 m_bg.m_position.y.fromString(res[1], _t("top;bottom;center"));
\r
1479 if(m_bg.m_position.x.is_predefined())
\r
1481 switch(m_bg.m_position.x.predef())
\r
1484 m_bg.m_position.x.set_value(0, css_units_percentage);
\r
1487 m_bg.m_position.x.set_value(100, css_units_percentage);
\r
1490 m_bg.m_position.x.set_value(50, css_units_percentage);
\r
1494 if(m_bg.m_position.y.is_predefined())
\r
1496 switch(m_bg.m_position.y.predef())
\r
1499 m_bg.m_position.y.set_value(0, css_units_percentage);
\r
1502 m_bg.m_position.y.set_value(100, css_units_percentage);
\r
1505 m_bg.m_position.y.set_value(50, css_units_percentage);
\r
1511 m_bg.m_position.x.set_value(0, css_units_percentage);
\r
1512 m_bg.m_position.y.set_value(0, css_units_percentage);
\r
1516 m_bg.m_position.y.set_value(0, css_units_percentage);
\r
1517 m_bg.m_position.x.set_value(0, css_units_percentage);
\r
1520 str = get_style_property(_t("background-size"), false, _t("auto"));
\r
1523 string_vector res;
\r
1524 split_string(str, res, _t(" \t"));
\r
1527 m_bg.m_position.width.fromString(res[0], background_size_strings);
\r
1528 if(res.size() > 1)
\r
1530 m_bg.m_position.height.fromString(res[1], background_size_strings);
\r
1533 m_bg.m_position.height.predef(background_size_auto);
\r
1537 m_bg.m_position.width.predef(background_size_auto);
\r
1538 m_bg.m_position.height.predef(background_size_auto);
\r
1542 document::ptr doc = get_document();
\r
1544 doc->cvt_units(m_bg.m_position.x, m_font_size);
\r
1545 doc->cvt_units(m_bg.m_position.y, m_font_size);
\r
1546 doc->cvt_units(m_bg.m_position.width, m_font_size);
\r
1547 doc->cvt_units(m_bg.m_position.height, m_font_size);
\r
1549 // parse background_attachment
\r
1550 m_bg.m_attachment = (background_attachment) value_index(
\r
1551 get_style_property(_t("background-attachment"), false, _t("scroll")),
\r
1552 background_attachment_strings,
\r
1553 background_attachment_scroll);
\r
1555 // parse background_attachment
\r
1556 m_bg.m_repeat = (background_repeat) value_index(
\r
1557 get_style_property(_t("background-repeat"), false, _t("repeat")),
\r
1558 background_repeat_strings,
\r
1559 background_repeat_repeat);
\r
1561 // parse background_clip
\r
1562 m_bg.m_clip = (background_box) value_index(
\r
1563 get_style_property(_t("background-clip"), false, _t("border-box")),
\r
1564 background_box_strings,
\r
1565 background_box_border);
\r
1567 // parse background_origin
\r
1568 m_bg.m_origin = (background_box) value_index(
\r
1569 get_style_property(_t("background-origin"), false, _t("padding-box")),
\r
1570 background_box_strings,
\r
1571 background_box_content);
\r
1573 // parse background-image
\r
1574 css::parse_css_url(get_style_property(_t("background-image"), false, _t("")), m_bg.m_image);
\r
1575 m_bg.m_baseurl = get_style_property(_t("background-image-baseurl"), false, _t(""));
\r
1577 if(!m_bg.m_image.empty())
\r
1579 doc->container()->load_image(m_bg.m_image.c_str(), m_bg.m_baseurl.empty() ? 0 : m_bg.m_baseurl.c_str(), true);
\r
1583 void litehtml::html_tag::add_positioned(const element::ptr &el)
\r
1585 if (m_el_position != element_position_static || (!have_parent()))
\r
1587 m_positioned.push_back(el);
\r
1590 element::ptr el_parent = parent();
\r
1593 el_parent->add_positioned(el);
\r
1598 void litehtml::html_tag::calc_outlines( int parent_width )
\r
1600 m_padding.left = m_css_padding.left.calc_percent(parent_width);
\r
1601 m_padding.right = m_css_padding.right.calc_percent(parent_width);
\r
1603 m_borders.left = m_css_borders.left.width.calc_percent(parent_width);
\r
1604 m_borders.right = m_css_borders.right.width.calc_percent(parent_width);
\r
1606 m_margins.left = m_css_margins.left.calc_percent(parent_width);
\r
1607 m_margins.right = m_css_margins.right.calc_percent(parent_width);
\r
1609 m_margins.top = m_css_margins.top.calc_percent(parent_width);
\r
1610 m_margins.bottom = m_css_margins.bottom.calc_percent(parent_width);
\r
1612 m_padding.top = m_css_padding.top.calc_percent(parent_width);
\r
1613 m_padding.bottom = m_css_padding.bottom.calc_percent(parent_width);
\r
1616 void litehtml::html_tag::calc_auto_margins(int parent_width)
\r
1618 if (get_element_position() != element_position_absolute && (m_display == display_block || m_display == display_table))
\r
1620 if (m_css_margins.left.is_predefined() && m_css_margins.right.is_predefined())
\r
1622 int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right;
\r
1623 if (el_width <= parent_width)
\r
1625 m_margins.left = (parent_width - el_width) / 2;
\r
1626 m_margins.right = (parent_width - el_width) - m_margins.left;
\r
1630 m_margins.left = 0;
\r
1631 m_margins.right = 0;
\r
1634 else if (m_css_margins.left.is_predefined() && !m_css_margins.right.is_predefined())
\r
1636 int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.right;
\r
1637 m_margins.left = parent_width - el_width;
\r
1638 if (m_margins.left < 0) m_margins.left = 0;
\r
1640 else if (!m_css_margins.left.is_predefined() && m_css_margins.right.is_predefined())
\r
1642 int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.left;
\r
1643 m_margins.right = parent_width - el_width;
\r
1644 if (m_margins.right < 0) m_margins.right = 0;
\r
1649 void litehtml::html_tag::parse_attributes()
\r
1651 for(auto& el : m_children)
\r
1653 el->parse_attributes();
\r
1657 void litehtml::html_tag::get_text( tstring& text )
\r
1659 for (auto& el : m_children)
\r
1661 el->get_text(text);
\r
1665 bool litehtml::html_tag::is_body() const
\r
1670 void litehtml::html_tag::set_data( const tchar_t* data )
\r
1675 void litehtml::html_tag::get_inline_boxes( position::vector& boxes )
\r
1677 litehtml::box* old_box = 0;
\r
1679 for(auto& el : m_children)
\r
1685 if(el->m_box != old_box)
\r
1691 pos.x -= m_padding.left + m_borders.left;
\r
1692 pos.width += m_padding.left + m_borders.left;
\r
1694 boxes.push_back(pos);
\r
1696 old_box = el->m_box;
\r
1697 pos.x = el->left() + el->margin_left();
\r
1698 pos.y = el->top() - m_padding.top - m_borders.top;
\r
1702 pos.width = el->right() - pos.x - el->margin_right() - el->margin_left();
\r
1703 pos.height = std::max(pos.height, el->height() + m_padding.top + m_padding.bottom + m_borders.top + m_borders.bottom);
\r
1704 } else if(el->get_display() == display_inline)
\r
1706 position::vector sub_boxes;
\r
1707 el->get_inline_boxes(sub_boxes);
\r
1708 if(!sub_boxes.empty())
\r
1710 sub_boxes.rbegin()->width += el->margin_right();
\r
1713 if(m_padding.left + m_borders.left > 0)
\r
1715 position padding_box = (*sub_boxes.begin());
\r
1716 padding_box.x -= m_padding.left + m_borders.left + el->margin_left();
\r
1717 padding_box.width = m_padding.left + m_borders.left + el->margin_left();
\r
1718 boxes.push_back(padding_box);
\r
1722 sub_boxes.rbegin()->width += el->margin_right();
\r
1724 boxes.insert(boxes.end(), sub_boxes.begin(), sub_boxes.end());
\r
1729 if(pos.width || pos.height)
\r
1733 pos.x -= m_padding.left + m_borders.left;
\r
1734 pos.width += m_padding.left + m_borders.left;
\r
1736 boxes.push_back(pos);
\r
1738 if(!boxes.empty())
\r
1740 if(m_padding.right + m_borders.right > 0)
\r
1742 boxes.back().width += m_padding.right + m_borders.right;
\r
1747 bool litehtml::html_tag::on_mouse_over()
\r
1751 element::ptr el = shared_from_this();
\r
1754 if(el->set_pseudo_class(_t("hover"), true))
\r
1758 el = el->parent();
\r
1764 bool litehtml::html_tag::find_styles_changes( position::vector& redraw_boxes, int x, int y )
\r
1766 if(m_display == display_inline_text)
\r
1772 bool apply = false;
\r
1773 for (used_selector::vector::iterator iter = m_used_styles.begin(); iter != m_used_styles.end() && !apply; iter++)
\r
1775 if((*iter)->m_selector->is_media_valid())
\r
1777 int res = select(*((*iter)->m_selector), true);
\r
1778 if( (res == select_no_match && (*iter)->m_used) || (res == select_match && !(*iter)->m_used) )
\r
1787 if(m_display == display_inline || m_display == display_table_row)
\r
1789 position::vector boxes;
\r
1790 get_inline_boxes(boxes);
\r
1791 for(position::vector::iterator pos = boxes.begin(); pos != boxes.end(); pos++)
\r
1795 redraw_boxes.push_back(*pos);
\r
1799 position pos = m_pos;
\r
1800 if(m_el_position != element_position_fixed)
\r
1807 redraw_boxes.push_back(pos);
\r
1814 for (auto& el : m_children)
\r
1818 if(m_el_position != element_position_fixed)
\r
1820 if(el->find_styles_changes(redraw_boxes, x + m_pos.x, y + m_pos.y))
\r
1826 if(el->find_styles_changes(redraw_boxes, m_pos.x, m_pos.y))
\r
1836 bool litehtml::html_tag::on_mouse_leave()
\r
1840 element::ptr el = shared_from_this();
\r
1843 if(el->set_pseudo_class(_t("hover"), false))
\r
1847 if(el->set_pseudo_class(_t("active"), false))
\r
1851 el = el->parent();
\r
1857 bool litehtml::html_tag::on_lbutton_down()
\r
1861 element::ptr el = shared_from_this();
\r
1864 if (el->set_pseudo_class(_t("active"), true))
\r
1868 el = el->parent();
\r
1874 bool litehtml::html_tag::on_lbutton_up()
\r
1878 element::ptr el = shared_from_this();
\r
1881 if (el->set_pseudo_class(_t("active"), false))
\r
1885 el = el->parent();
\r
1893 void litehtml::html_tag::on_click()
\r
1895 if (have_parent())
\r
1897 element::ptr el_parent = parent();
\r
1900 el_parent->on_click();
\r
1905 const litehtml::tchar_t* litehtml::html_tag::get_cursor()
\r
1907 return get_style_property(_t("cursor"), true, 0);
\r
1910 static const int font_size_table[8][7] =
\r
1912 { 9, 9, 9, 9, 11, 14, 18},
\r
1913 { 9, 9, 9, 10, 12, 15, 20},
\r
1914 { 9, 9, 9, 11, 13, 17, 22},
\r
1915 { 9, 9, 10, 12, 14, 18, 24},
\r
1916 { 9, 9, 10, 13, 16, 20, 26},
\r
1917 { 9, 9, 11, 14, 17, 21, 28},
\r
1918 { 9, 10, 12, 15, 17, 23, 30},
\r
1919 { 9, 10, 13, 16, 18, 24, 32}
\r
1923 void litehtml::html_tag::init_font()
\r
1925 // initialize font size
\r
1926 const tchar_t* str = get_style_property(_t("font-size"), false, 0);
\r
1928 int parent_sz = 0;
\r
1929 int doc_font_size = get_document()->container()->get_default_font_size();
\r
1930 element::ptr el_parent = parent();
\r
1933 parent_sz = el_parent->get_font_size();
\r
1936 parent_sz = doc_font_size;
\r
1942 m_font_size = parent_sz;
\r
1945 m_font_size = parent_sz;
\r
1948 sz.fromString(str, font_size_strings);
\r
1949 if(sz.is_predefined())
\r
1951 int idx_in_table = doc_font_size - 9;
\r
1952 if(idx_in_table >= 0 && idx_in_table <= 7)
\r
1954 if(sz.predef() >= fontSize_xx_small && sz.predef() <= fontSize_xx_large)
\r
1956 m_font_size = font_size_table[idx_in_table][sz.predef()];
\r
1959 m_font_size = doc_font_size;
\r
1963 switch(sz.predef())
\r
1965 case fontSize_xx_small:
\r
1966 m_font_size = doc_font_size * 3 / 5;
\r
1968 case fontSize_x_small:
\r
1969 m_font_size = doc_font_size * 3 / 4;
\r
1971 case fontSize_small:
\r
1972 m_font_size = doc_font_size * 8 / 9;
\r
1974 case fontSize_large:
\r
1975 m_font_size = doc_font_size * 6 / 5;
\r
1977 case fontSize_x_large:
\r
1978 m_font_size = doc_font_size * 3 / 2;
\r
1980 case fontSize_xx_large:
\r
1981 m_font_size = doc_font_size * 2;
\r
1984 m_font_size = doc_font_size;
\r
1990 if(sz.units() == css_units_percentage)
\r
1992 m_font_size = sz.calc_percent(parent_sz);
\r
1993 } else if(sz.units() == css_units_none)
\r
1995 m_font_size = parent_sz;
\r
1998 m_font_size = get_document()->cvt_units(sz, parent_sz);
\r
2003 // initialize font
\r
2004 const tchar_t* name = get_style_property(_t("font-family"), true, _t("inherit"));
\r
2005 const tchar_t* weight = get_style_property(_t("font-weight"), true, _t("normal"));
\r
2006 const tchar_t* style = get_style_property(_t("font-style"), true, _t("normal"));
\r
2007 const tchar_t* decoration = get_style_property(_t("text-decoration"), true, _t("none"));
\r
2009 m_font = get_document()->get_font(name, m_font_size, weight, style, decoration, &m_font_metrics);
\r
2012 bool litehtml::html_tag::is_break() const
\r
2017 void litehtml::html_tag::set_tagName( const tchar_t* tag )
\r
2019 tstring s_val = tag;
\r
2020 std::locale lc = std::locale::global(std::locale::classic());
\r
2021 for(size_t i = 0; i < s_val.length(); i++)
\r
2023 s_val[i] = std::tolower(s_val[i], lc);
\r
2028 void litehtml::html_tag::draw_background( uint_ptr hdc, int x, int y, const position* clip )
\r
2030 position pos = m_pos;
\r
2034 position el_pos = pos;
\r
2035 el_pos += m_padding;
\r
2036 el_pos += m_borders;
\r
2038 if(m_display != display_inline && m_display != display_table_row)
\r
2040 if(el_pos.does_intersect(clip))
\r
2042 const background* bg = get_background();
\r
2045 background_paint bg_paint;
\r
2046 init_background_paint(pos, bg_paint, bg);
\r
2048 get_document()->container()->draw_background(hdc, bg_paint);
\r
2050 position border_box = pos;
\r
2051 border_box += m_padding;
\r
2052 border_box += m_borders;
\r
2054 borders bdr = m_css_borders;
\r
2055 bdr.radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);
\r
2057 get_document()->container()->draw_borders(hdc, bdr, border_box, have_parent() ? false : true);
\r
2061 const background* bg = get_background();
\r
2063 position::vector boxes;
\r
2064 get_inline_boxes(boxes);
\r
2066 background_paint bg_paint;
\r
2067 position content_box;
\r
2069 for(position::vector::iterator box = boxes.begin(); box != boxes.end(); box++)
\r
2074 if(box->does_intersect(clip))
\r
2076 content_box = *box;
\r
2077 content_box -= m_borders;
\r
2078 content_box -= m_padding;
\r
2082 init_background_paint(content_box, bg_paint, bg);
\r
2087 // set left borders radius for the first box
\r
2088 if(box == boxes.begin())
\r
2090 bdr.radius.bottom_left_x = m_css_borders.radius.bottom_left_x;
\r
2091 bdr.radius.bottom_left_y = m_css_borders.radius.bottom_left_y;
\r
2092 bdr.radius.top_left_x = m_css_borders.radius.top_left_x;
\r
2093 bdr.radius.top_left_y = m_css_borders.radius.top_left_y;
\r
2096 // set right borders radius for the last box
\r
2097 if(box == boxes.end() - 1)
\r
2099 bdr.radius.bottom_right_x = m_css_borders.radius.bottom_right_x;
\r
2100 bdr.radius.bottom_right_y = m_css_borders.radius.bottom_right_y;
\r
2101 bdr.radius.top_right_x = m_css_borders.radius.top_right_x;
\r
2102 bdr.radius.top_right_y = m_css_borders.radius.top_right_y;
\r
2106 bdr.top = m_css_borders.top;
\r
2107 bdr.bottom = m_css_borders.bottom;
\r
2108 if(box == boxes.begin())
\r
2110 bdr.left = m_css_borders.left;
\r
2112 if(box == boxes.end() - 1)
\r
2114 bdr.right = m_css_borders.right;
\r
2120 bg_paint.border_radius = bdr.radius.calc_percents(bg_paint.border_box.width, bg_paint.border_box.width);
\r
2121 get_document()->container()->draw_background(hdc, bg_paint);
\r
2124 b.radius = bdr.radius.calc_percents(box->width, box->height);
\r
2125 get_document()->container()->draw_borders(hdc, b, *box, false);
\r
2131 int litehtml::html_tag::render_inline(const element::ptr &container, int max_width)
\r
2133 int ret_width = 0;
\r
2136 white_space ws = get_white_space();
\r
2137 bool skip_spaces = false;
\r
2138 if (ws == white_space_normal ||
\r
2139 ws == white_space_nowrap ||
\r
2140 ws == white_space_pre_line)
\r
2142 skip_spaces = true;
\r
2144 bool was_space = false;
\r
2146 for (auto& el : m_children)
\r
2148 // skip spaces to make rendering a bit faster
\r
2151 if (el->is_white_space())
\r
2165 was_space = false;
\r
2169 rw = container->place_element( el, max_width );
\r
2170 if(rw > ret_width)
\r
2178 int litehtml::html_tag::place_element(const element::ptr &el, int max_width)
\r
2180 if(el->get_display() == display_none) return 0;
\r
2182 if(el->get_display() == display_inline)
\r
2184 return el->render_inline(shared_from_this(), max_width);
\r
2187 element_position el_position = el->get_element_position();
\r
2189 if(el_position == element_position_absolute || el_position == element_position_fixed)
\r
2192 if(!m_boxes.empty())
\r
2194 if(m_boxes.back()->get_type() == box_line)
\r
2196 line_top = m_boxes.back()->top();
\r
2197 if(!m_boxes.back()->is_empty())
\r
2199 line_top += line_height();
\r
2203 line_top = m_boxes.back()->bottom();
\r
2207 el->render(0, line_top, max_width);
\r
2208 el->m_pos.x += el->content_margins_left();
\r
2209 el->m_pos.y += el->content_margins_top();
\r
2214 int ret_width = 0;
\r
2216 switch(el->get_float())
\r
2221 if(!m_boxes.empty())
\r
2223 if(m_boxes.back()->get_type() == box_line)
\r
2225 line_top = m_boxes.back()->top();
\r
2228 line_top = m_boxes.back()->bottom();
\r
2231 line_top = get_cleared_top(el, line_top);
\r
2232 int line_left = 0;
\r
2233 int line_right = max_width;
\r
2234 get_line_left_right(line_top, max_width, line_left, line_right);
\r
2236 el->render(line_left, line_top, line_right);
\r
2237 if(el->right() > line_right)
\r
2239 int new_top = find_next_line_top(el->top(), el->width(), max_width);
\r
2240 el->m_pos.x = get_line_left(new_top) + el->content_margins_left();
\r
2241 el->m_pos.y = new_top + el->content_margins_top();
\r
2243 add_float(el, 0, 0);
\r
2244 ret_width = fix_line_width(max_width, float_left);
\r
2247 ret_width = el->right();
\r
2254 if(!m_boxes.empty())
\r
2256 if(m_boxes.back()->get_type() == box_line)
\r
2258 line_top = m_boxes.back()->top();
\r
2261 line_top = m_boxes.back()->bottom();
\r
2264 line_top = get_cleared_top(el, line_top);
\r
2265 int line_left = 0;
\r
2266 int line_right = max_width;
\r
2267 get_line_left_right(line_top, max_width, line_left, line_right);
\r
2269 el->render(0, line_top, line_right);
\r
2271 if(line_left + el->width() > line_right)
\r
2273 int new_top = find_next_line_top(el->top(), el->width(), max_width);
\r
2274 el->m_pos.x = get_line_right(new_top, max_width) - el->width() + el->content_margins_left();
\r
2275 el->m_pos.y = new_top + el->content_margins_top();
\r
2278 el->m_pos.x = line_right - el->width() + el->content_margins_left();
\r
2280 add_float(el, 0, 0);
\r
2281 ret_width = fix_line_width(max_width, float_right);
\r
2286 line_right = max_width;
\r
2287 get_line_left_right(line_top, max_width, line_left, line_right);
\r
2289 ret_width = ret_width + (max_width - line_right);
\r
2295 line_context line_ctx;
\r
2297 if (!m_boxes.empty())
\r
2299 line_ctx.top = m_boxes.back()->top();
\r
2301 line_ctx.left = 0;
\r
2302 line_ctx.right = max_width;
\r
2303 line_ctx.fix_top();
\r
2304 get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
\r
2306 switch(el->get_display())
\r
2308 case display_inline_block:
\r
2309 ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.right);
\r
2311 case display_block:
\r
2312 if(el->is_replaced() || el->is_floats_holder())
\r
2314 element::ptr el_parent = el->parent();
\r
2315 el->m_pos.width = el->get_css_width().calc_percent(line_ctx.right - line_ctx.left);
\r
2316 el->m_pos.height = el->get_css_height().calc_percent(el_parent ? el_parent->m_pos.height : 0);
\r
2318 el->calc_outlines(line_ctx.right - line_ctx.left);
\r
2320 case display_inline_text:
\r
2322 litehtml::size sz;
\r
2323 el->get_content_size(sz, line_ctx.right);
\r
2332 bool add_box = true;
\r
2333 if(!m_boxes.empty())
\r
2335 if(m_boxes.back()->can_hold(el, m_white_space))
\r
2342 new_box(el, max_width, line_ctx);
\r
2343 } else if(!m_boxes.empty())
\r
2345 line_ctx.top = m_boxes.back()->top();
\r
2348 if (line_ctx.top != line_ctx.calculatedTop)
\r
2350 line_ctx.left = 0;
\r
2351 line_ctx.right = max_width;
\r
2352 line_ctx.fix_top();
\r
2353 get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
\r
2356 if(!el->is_inline_box())
\r
2358 if(m_boxes.size() == 1)
\r
2360 if(collapse_top_margin())
\r
2362 int shift = el->margin_top();
\r
2365 line_ctx.top -= shift;
\r
2366 m_boxes.back()->y_shift(-shift);
\r
2372 int prev_margin = m_boxes[m_boxes.size() - 2]->bottom_margin();
\r
2374 if(prev_margin > el->margin_top())
\r
2376 shift = el->margin_top();
\r
2379 shift = prev_margin;
\r
2383 line_ctx.top -= shift;
\r
2384 m_boxes.back()->y_shift(-shift);
\r
2389 switch(el->get_display())
\r
2391 case display_table:
\r
2392 case display_list_item:
\r
2393 ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.width());
\r
2395 case display_block:
\r
2396 case display_table_cell:
\r
2397 case display_table_caption:
\r
2398 case display_table_row:
\r
2399 if(el->is_replaced() || el->is_floats_holder())
\r
2401 ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.width()) + line_ctx.left + (max_width - line_ctx.right);
\r
2404 ret_width = el->render(0, line_ctx.top, max_width);
\r
2412 m_boxes.back()->add_element(el);
\r
2414 if(el->is_inline_box() && !el->skip())
\r
2416 ret_width = el->right() + (max_width - line_ctx.right);
\r
2425 bool litehtml::html_tag::set_pseudo_class( const tchar_t* pclass, bool add )
\r
2430 if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), pclass) == m_pseudo_classes.end())
\r
2432 m_pseudo_classes.push_back(pclass);
\r
2437 string_vector::iterator pi = std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), pclass);
\r
2438 if(pi != m_pseudo_classes.end())
\r
2440 m_pseudo_classes.erase(pi);
\r
2447 bool litehtml::html_tag::set_class( const tchar_t* pclass, bool add )
\r
2449 string_vector classes;
\r
2450 bool changed = false;
\r
2452 split_string( pclass, classes, _t(" ") );
\r
2456 for( auto & _class : classes )
\r
2458 if(std::find(m_class_values.begin(), m_class_values.end(), _class) == m_class_values.end())
\r
2460 m_class_values.push_back( std::move( _class ) );
\r
2466 for( const auto & _class : classes )
\r
2468 auto end = std::remove(m_class_values.begin(), m_class_values.end(), _class);
\r
2470 if(end != m_class_values.end())
\r
2472 m_class_values.erase(end, m_class_values.end());
\r
2480 tstring class_string;
\r
2481 join_string(class_string, m_class_values, _t(" "));
\r
2482 set_attr(_t("class"), class_string.c_str());
\r
2493 int litehtml::html_tag::line_height() const
\r
2495 return m_line_height;
\r
2498 bool litehtml::html_tag::is_replaced() const
\r
2503 int litehtml::html_tag::finish_last_box(bool end_of_render)
\r
2507 if(!m_boxes.empty())
\r
2509 m_boxes.back()->finish(end_of_render);
\r
2511 if(m_boxes.back()->is_empty())
\r
2513 line_top = m_boxes.back()->top();
\r
2514 m_boxes.pop_back();
\r
2517 if(!m_boxes.empty())
\r
2519 line_top = m_boxes.back()->bottom();
\r
2525 int litehtml::html_tag::new_box(const element::ptr &el, int max_width, line_context& line_ctx)
\r
2527 line_ctx.top = get_cleared_top(el, finish_last_box());
\r
2529 line_ctx.left = 0;
\r
2530 line_ctx.right = max_width;
\r
2531 line_ctx.fix_top();
\r
2532 get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
\r
2534 if(el->is_inline_box() || el->is_floats_holder())
\r
2536 if (el->width() > line_ctx.right - line_ctx.left)
\r
2538 line_ctx.top = find_next_line_top(line_ctx.top, el->width(), max_width);
\r
2539 line_ctx.left = 0;
\r
2540 line_ctx.right = max_width;
\r
2541 line_ctx.fix_top();
\r
2542 get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
\r
2546 int first_line_margin = 0;
\r
2547 if(m_boxes.empty() && m_list_style_type != list_style_type_none && m_list_style_position == list_style_position_inside)
\r
2549 int sz_font = get_font_size();
\r
2550 first_line_margin = sz_font;
\r
2553 if(el->is_inline_box())
\r
2555 int text_indent = 0;
\r
2556 if(m_css_text_indent.val() != 0)
\r
2558 bool line_box_found = false;
\r
2559 for(box::vector::iterator iter = m_boxes.begin(); iter != m_boxes.end(); iter++)
\r
2561 if((*iter)->get_type() == box_line)
\r
2563 line_box_found = true;
\r
2567 if(!line_box_found)
\r
2569 text_indent = m_css_text_indent.calc_percent(max_width);
\r
2575 m_boxes.emplace_back(std::unique_ptr<line_box>(new line_box(line_ctx.top, line_ctx.left + first_line_margin + text_indent, line_ctx.right, line_height(), fm, m_text_align)));
\r
2578 m_boxes.emplace_back(std::unique_ptr<block_box>(new block_box(line_ctx.top, line_ctx.left, line_ctx.right)));
\r
2581 return line_ctx.top;
\r
2584 int litehtml::html_tag::get_cleared_top(const element::ptr &el, int line_top) const
\r
2586 switch(el->get_clear())
\r
2590 int fh = get_left_floats_height();
\r
2591 if(fh && fh > line_top)
\r
2599 int fh = get_right_floats_height();
\r
2600 if(fh && fh > line_top)
\r
2608 int fh = get_floats_height();
\r
2609 if(fh && fh > line_top)
\r
2616 if(el->get_float() != float_none)
\r
2618 int fh = get_floats_height(el->get_float());
\r
2619 if(fh && fh > line_top)
\r
2629 litehtml::style_display litehtml::html_tag::get_display() const
\r
2634 litehtml::element_float litehtml::html_tag::get_float() const
\r
2639 bool litehtml::html_tag::is_floats_holder() const
\r
2641 if( m_display == display_inline_block ||
\r
2642 m_display == display_table_cell ||
\r
2645 m_float != float_none ||
\r
2646 m_el_position == element_position_absolute ||
\r
2647 m_el_position == element_position_fixed ||
\r
2648 m_overflow > overflow_visible)
\r
2655 bool litehtml::html_tag::is_first_child_inline(const element::ptr& el) const
\r
2657 if(!m_children.empty())
\r
2659 for (const auto& this_el : m_children)
\r
2661 if (!this_el->is_white_space())
\r
2663 if (el == this_el)
\r
2667 if (this_el->get_display() == display_inline)
\r
2669 if (this_el->have_inline_child())
\r
2683 bool litehtml::html_tag::is_last_child_inline(const element::ptr& el)
\r
2685 if(!m_children.empty())
\r
2687 for (auto this_el = m_children.rbegin(); this_el < m_children.rend(); ++this_el)
\r
2689 if (!(*this_el)->is_white_space())
\r
2691 if (el == (*this_el))
\r
2695 if ((*this_el)->get_display() == display_inline)
\r
2697 if ((*this_el)->have_inline_child())
\r
2711 litehtml::white_space litehtml::html_tag::get_white_space() const
\r
2713 return m_white_space;
\r
2716 litehtml::vertical_align litehtml::html_tag::get_vertical_align() const
\r
2718 return m_vertical_align;
\r
2721 litehtml::css_length litehtml::html_tag::get_css_left() const
\r
2723 return m_css_offsets.left;
\r
2726 litehtml::css_length litehtml::html_tag::get_css_right() const
\r
2728 return m_css_offsets.right;
\r
2731 litehtml::css_length litehtml::html_tag::get_css_top() const
\r
2733 return m_css_offsets.top;
\r
2736 litehtml::css_length litehtml::html_tag::get_css_bottom() const
\r
2738 return m_css_offsets.bottom;
\r
2742 litehtml::css_offsets litehtml::html_tag::get_css_offsets() const
\r
2744 return m_css_offsets;
\r
2747 litehtml::element_clear litehtml::html_tag::get_clear() const
\r
2752 litehtml::css_length litehtml::html_tag::get_css_width() const
\r
2754 return m_css_width;
\r
2757 litehtml::css_length litehtml::html_tag::get_css_height() const
\r
2759 return m_css_height;
\r
2762 size_t litehtml::html_tag::get_children_count() const
\r
2764 return m_children.size();
\r
2767 litehtml::element::ptr litehtml::html_tag::get_child( int idx ) const
\r
2769 return m_children[idx];
\r
2772 void litehtml::html_tag::set_css_width( css_length& w )
\r
2777 void litehtml::html_tag::apply_vertical_align()
\r
2779 if(!m_boxes.empty())
\r
2782 int content_height = m_boxes.back()->bottom();
\r
2784 if(m_pos.height > content_height)
\r
2786 switch(m_vertical_align)
\r
2789 add = (m_pos.height - content_height) / 2;
\r
2792 add = m_pos.height - content_height;
\r
2802 for(size_t i = 0; i < m_boxes.size(); i++)
\r
2804 m_boxes[i]->y_shift(add);
\r
2810 litehtml::element_position litehtml::html_tag::get_element_position(css_offsets* offsets) const
\r
2812 if(offsets && m_el_position != element_position_static)
\r
2814 *offsets = m_css_offsets;
\r
2816 return m_el_position;
\r
2819 void litehtml::html_tag::init_background_paint(position pos, background_paint &bg_paint, const background* bg)
\r
2824 position content_box = pos;
\r
2825 position padding_box = pos;
\r
2826 padding_box += m_padding;
\r
2827 position border_box = padding_box;
\r
2828 border_box += m_borders;
\r
2830 switch(bg->m_clip)
\r
2832 case litehtml::background_box_padding:
\r
2833 bg_paint.clip_box = padding_box;
\r
2835 case litehtml::background_box_content:
\r
2836 bg_paint.clip_box = content_box;
\r
2839 bg_paint.clip_box = border_box;
\r
2843 switch(bg->m_origin)
\r
2845 case litehtml::background_box_border:
\r
2846 bg_paint.origin_box = border_box;
\r
2848 case litehtml::background_box_content:
\r
2849 bg_paint.origin_box = content_box;
\r
2852 bg_paint.origin_box = padding_box;
\r
2856 if(!bg_paint.image.empty())
\r
2858 get_document()->container()->get_image_size(bg_paint.image.c_str(), bg_paint.baseurl.c_str(), bg_paint.image_size);
\r
2859 if(bg_paint.image_size.width && bg_paint.image_size.height)
\r
2861 litehtml::size img_new_sz = bg_paint.image_size;
\r
2862 double img_ar_width = (double) bg_paint.image_size.width / (double) bg_paint.image_size.height;
\r
2863 double img_ar_height = (double) bg_paint.image_size.height / (double) bg_paint.image_size.width;
\r
2866 if(bg->m_position.width.is_predefined())
\r
2868 switch(bg->m_position.width.predef())
\r
2870 case litehtml::background_size_contain:
\r
2871 if( (int) ((double) bg_paint.origin_box.width * img_ar_height) <= bg_paint.origin_box.height )
\r
2873 img_new_sz.width = bg_paint.origin_box.width;
\r
2874 img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height);
\r
2877 img_new_sz.height = bg_paint.origin_box.height;
\r
2878 img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width);
\r
2881 case litehtml::background_size_cover:
\r
2882 if( (int) ((double) bg_paint.origin_box.width * img_ar_height) >= bg_paint.origin_box.height )
\r
2884 img_new_sz.width = bg_paint.origin_box.width;
\r
2885 img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height);
\r
2888 img_new_sz.height = bg_paint.origin_box.height;
\r
2889 img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width);
\r
2893 case litehtml::background_size_auto:
\r
2894 if(!bg->m_position.height.is_predefined())
\r
2896 img_new_sz.height = bg->m_position.height.calc_percent(bg_paint.origin_box.height);
\r
2897 img_new_sz.width = (int) ((double) img_new_sz.height * img_ar_width);
\r
2903 img_new_sz.width = bg->m_position.width.calc_percent(bg_paint.origin_box.width);
\r
2904 if(bg->m_position.height.is_predefined())
\r
2906 img_new_sz.height = (int) ((double) img_new_sz.width * img_ar_height);
\r
2909 img_new_sz.height = bg->m_position.height.calc_percent(bg_paint.origin_box.height);
\r
2913 bg_paint.image_size = img_new_sz;
\r
2914 bg_paint.position_x = bg_paint.origin_box.x + (int) bg->m_position.x.calc_percent(bg_paint.origin_box.width - bg_paint.image_size.width);
\r
2915 bg_paint.position_y = bg_paint.origin_box.y + (int) bg->m_position.y.calc_percent(bg_paint.origin_box.height - bg_paint.image_size.height);
\r
2919 bg_paint.border_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);;
\r
2920 bg_paint.border_box = border_box;
\r
2921 bg_paint.is_root = have_parent() ? false : true;
\r
2924 litehtml::visibility litehtml::html_tag::get_visibility() const
\r
2926 return m_visibility;
\r
2929 void litehtml::html_tag::draw_list_marker( uint_ptr hdc, const position &pos )
\r
2933 const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0);
\r
2937 css::parse_css_url(list_image, lm.image);
\r
2938 lm.baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0);
\r
2939 get_document()->container()->get_image_size(lm.image.c_str(), lm.baseurl, img_size);
\r
2946 int ln_height = line_height();
\r
2947 int sz_font = get_font_size();
\r
2949 lm.pos.width = sz_font - sz_font * 2 / 3;
\r
2950 lm.pos.height = sz_font - sz_font * 2 / 3;
\r
2951 lm.pos.y = pos.y + ln_height / 2 - lm.pos.height / 2;
\r
2953 if(img_size.width && img_size.height)
\r
2955 if(lm.pos.y + img_size.height > pos.y + pos.height)
\r
2957 lm.pos.y = pos.y + pos.height - img_size.height;
\r
2959 if(img_size.width > lm.pos.width)
\r
2961 lm.pos.x -= img_size.width - lm.pos.width;
\r
2964 lm.pos.width = img_size.width;
\r
2965 lm.pos.height = img_size.height;
\r
2967 if(m_list_style_position == list_style_position_outside)
\r
2969 lm.pos.x -= sz_font;
\r
2972 lm.color = get_color(_t("color"), true, web_color(0, 0, 0));
\r
2973 lm.marker_type = m_list_style_type;
\r
2974 get_document()->container()->draw_list_marker(hdc, lm);
\r
2977 void litehtml::html_tag::draw_children( uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex )
\r
2979 if (m_display == display_table || m_display == display_inline_table)
\r
2981 draw_children_table(hdc, x, y, clip, flag, zindex);
\r
2985 draw_children_box(hdc, x, y, clip, flag, zindex);
\r
2989 bool litehtml::html_tag::fetch_positioned()
\r
2993 m_positioned.clear();
\r
2995 litehtml::element_position el_pos;
\r
2997 for(auto& el : m_children)
\r
2999 el_pos = el->get_element_position();
\r
3000 if (el_pos != element_position_static)
\r
3002 add_positioned(el);
\r
3004 if (!ret && (el_pos == element_position_absolute || el_pos == element_position_fixed))
\r
3008 if(el->fetch_positioned())
\r
3016 int litehtml::html_tag::get_zindex() const
\r
3021 void litehtml::html_tag::render_positioned(render_type rt)
\r
3023 position wnd_position;
\r
3024 get_document()->container()->get_client_rect(wnd_position);
\r
3026 element_position el_position;
\r
3028 for (auto& el : m_positioned)
\r
3030 el_position = el->get_element_position();
\r
3033 if(el->get_display() != display_none)
\r
3035 if(el_position == element_position_absolute)
\r
3037 if(rt != render_fixed_only)
\r
3041 } else if(el_position == element_position_fixed)
\r
3043 if(rt != render_no_fixed)
\r
3052 int parent_height = 0;
\r
3053 int parent_width = 0;
\r
3056 if(el_position == element_position_fixed)
\r
3058 parent_height = wnd_position.height;
\r
3059 parent_width = wnd_position.width;
\r
3060 client_x = wnd_position.left();
\r
3061 client_y = wnd_position.top();
\r
3064 element::ptr el_parent = el->parent();
\r
3067 parent_height = el_parent->height();
\r
3068 parent_width = el_parent->width();
\r
3072 css_length css_left = el->get_css_left();
\r
3073 css_length css_right = el->get_css_right();
\r
3074 css_length css_top = el->get_css_top();
\r
3075 css_length css_bottom = el->get_css_bottom();
\r
3077 bool need_render = false;
\r
3079 css_length el_w = el->get_css_width();
\r
3080 css_length el_h = el->get_css_height();
\r
3082 int new_width = -1;
\r
3083 int new_height = -1;
\r
3084 if(el_w.units() == css_units_percentage && parent_width)
\r
3086 new_width = el_w.calc_percent(parent_width);
\r
3087 if(el->m_pos.width != new_width)
\r
3089 need_render = true;
\r
3090 el->m_pos.width = new_width;
\r
3094 if(el_h.units() == css_units_percentage && parent_height)
\r
3096 new_height = el_h.calc_percent(parent_height);
\r
3097 if(el->m_pos.height != new_height)
\r
3099 need_render = true;
\r
3100 el->m_pos.height = new_height;
\r
3104 bool cvt_x = false;
\r
3105 bool cvt_y = false;
\r
3107 if(el_position == element_position_fixed)
\r
3109 if(!css_left.is_predefined() || !css_right.is_predefined())
\r
3111 if(!css_left.is_predefined() && css_right.is_predefined())
\r
3113 el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left();
\r
3114 } else if(css_left.is_predefined() && !css_right.is_predefined())
\r
3116 el->m_pos.x = parent_width - css_right.calc_percent(parent_width) - el->m_pos.width - el->content_margins_right();
\r
3119 el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left();
\r
3120 el->m_pos.width = parent_width - css_left.calc_percent(parent_width) - css_right.calc_percent(parent_width) - (el->content_margins_left() + el->content_margins_right());
\r
3121 need_render = true;
\r
3125 if(!css_top.is_predefined() || !css_bottom.is_predefined())
\r
3127 if(!css_top.is_predefined() && css_bottom.is_predefined())
\r
3129 el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top();
\r
3130 } else if(css_top.is_predefined() && !css_bottom.is_predefined())
\r
3132 el->m_pos.y = parent_height - css_bottom.calc_percent(parent_height) - el->m_pos.height - el->content_margins_bottom();
\r
3135 el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top();
\r
3136 el->m_pos.height = parent_height - css_top.calc_percent(parent_height) - css_bottom.calc_percent(parent_height) - (el->content_margins_top() + el->content_margins_bottom());
\r
3137 need_render = true;
\r
3142 if(!css_left.is_predefined() || !css_right.is_predefined())
\r
3144 if(!css_left.is_predefined() && css_right.is_predefined())
\r
3146 el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left() - m_padding.left;
\r
3147 } else if(css_left.is_predefined() && !css_right.is_predefined())
\r
3149 el->m_pos.x = m_pos.width + m_padding.right - css_right.calc_percent(parent_width) - el->m_pos.width - el->content_margins_right();
\r
3152 el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left() - m_padding.left;
\r
3153 el->m_pos.width = m_pos.width + m_padding.left + m_padding.right - css_left.calc_percent(parent_width) - css_right.calc_percent(parent_width) - (el->content_margins_left() + el->content_margins_right());
\r
3154 if (new_width != -1)
\r
3156 el->m_pos.x += (el->m_pos.width - new_width) / 2;
\r
3157 el->m_pos.width = new_width;
\r
3159 need_render = true;
\r
3164 if(!css_top.is_predefined() || !css_bottom.is_predefined())
\r
3166 if(!css_top.is_predefined() && css_bottom.is_predefined())
\r
3168 el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top() - m_padding.top;
\r
3169 } else if(css_top.is_predefined() && !css_bottom.is_predefined())
\r
3171 el->m_pos.y = m_pos.height + m_padding.bottom - css_bottom.calc_percent(parent_height) - el->m_pos.height - el->content_margins_bottom();
\r
3174 el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top() - m_padding.top;
\r
3175 el->m_pos.height = m_pos.height + m_padding.top + m_padding.bottom - css_top.calc_percent(parent_height) - css_bottom.calc_percent(parent_height) - (el->content_margins_top() + el->content_margins_bottom());
\r
3176 if (new_height != -1)
\r
3178 el->m_pos.y += (el->m_pos.height - new_height) / 2;
\r
3179 el->m_pos.height = new_height;
\r
3181 need_render = true;
\r
3187 if(cvt_x || cvt_y)
\r
3191 element::ptr cur_el = el->parent();
\r
3192 element::ptr this_el = shared_from_this();
\r
3193 while(cur_el && cur_el != this_el)
\r
3195 offset_x += cur_el->m_pos.x;
\r
3196 offset_y += cur_el->m_pos.y;
\r
3197 cur_el = cur_el->parent();
\r
3199 if(cvt_x) el->m_pos.x -= offset_x;
\r
3200 if(cvt_y) el->m_pos.y -= offset_y;
\r
3205 position pos = el->m_pos;
\r
3206 el->render(el->left(), el->top(), el->width(), true);
\r
3210 if(el_position == element_position_fixed)
\r
3212 position fixed_pos;
\r
3213 el->get_redraw_box(fixed_pos);
\r
3214 get_document()->add_fixed_box(fixed_pos);
\r
3218 el->render_positioned();
\r
3221 if(!m_positioned.empty())
\r
3223 std::stable_sort(m_positioned.begin(), m_positioned.end(), [](const litehtml::element::ptr& _Left, const litehtml::element::ptr& _Right)
\r
3225 return (_Left->get_zindex() < _Right->get_zindex());
\r
3230 void litehtml::html_tag::draw_stacking_context( uint_ptr hdc, int x, int y, const position* clip, bool with_positioned )
\r
3232 if(!is_visible()) return;
\r
3234 std::map<int, bool> zindexes;
\r
3235 if(with_positioned)
\r
3237 for(elements_vector::iterator i = m_positioned.begin(); i != m_positioned.end(); i++)
\r
3239 zindexes[(*i)->get_zindex()];
\r
3242 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++)
\r
3244 if(idx->first < 0)
\r
3246 draw_children(hdc, x, y, clip, draw_positioned, idx->first);
\r
3250 draw_children(hdc, x, y, clip, draw_block, 0);
\r
3251 draw_children(hdc, x, y, clip, draw_floats, 0);
\r
3252 draw_children(hdc, x, y, clip, draw_inlines, 0);
\r
3253 if(with_positioned)
\r
3255 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++)
\r
3257 if(idx->first == 0)
\r
3259 draw_children(hdc, x, y, clip, draw_positioned, idx->first);
\r
3263 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++)
\r
3265 if(idx->first > 0)
\r
3267 draw_children(hdc, x, y, clip, draw_positioned, idx->first);
\r
3273 litehtml::overflow litehtml::html_tag::get_overflow() const
\r
3275 return m_overflow;
\r
3278 bool litehtml::html_tag::is_nth_child(const element::ptr& el, int num, int off, bool of_type) const
\r
3281 for(const auto& child : m_children)
\r
3283 if(child->get_display() != display_inline_text)
\r
3285 if( (!of_type) || (of_type && !t_strcmp(el->get_tagName(), child->get_tagName())) )
\r
3291 if((idx - off) >= 0 && (idx - off) % num == 0)
\r
3296 } else if(idx == off)
\r
3304 if(el == child) break;
\r
3310 bool litehtml::html_tag::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const
\r
3313 for(elements_vector::const_reverse_iterator child = m_children.rbegin(); child != m_children.rend(); child++)
\r
3315 if((*child)->get_display() != display_inline_text)
\r
3317 if( !of_type || (of_type && !t_strcmp(el->get_tagName(), (*child)->get_tagName())) )
\r
3319 if(el == (*child))
\r
3323 if((idx - off) >= 0 && (idx - off) % num == 0)
\r
3328 } else if(idx == off)
\r
3336 if(el == (*child)) break;
\r
3342 void litehtml::html_tag::parse_nth_child_params( tstring param, int &num, int &off )
\r
3344 if(param == _t("odd"))
\r
3348 } else if(param == _t("even"))
\r
3354 string_vector tokens;
\r
3355 split_string(param, tokens, _t(" n"), _t("n"));
\r
3361 for(string_vector::iterator tok = tokens.begin(); tok != tokens.end(); tok++)
\r
3363 if((*tok) == _t("n"))
\r
3374 num = t_atoi(s_num.c_str());
\r
3375 off = t_atoi(s_off.c_str());
\r
3379 void litehtml::html_tag::calc_document_size( litehtml::size& sz, int x /*= 0*/, int y /*= 0*/ )
\r
3381 if(is_visible() && m_el_position != element_position_fixed)
\r
3383 element::calc_document_size(sz, x, y);
\r
3385 if(m_overflow == overflow_visible)
\r
3387 for(auto& el : m_children)
\r
3389 el->calc_document_size(sz, x + m_pos.x, y + m_pos.y);
\r
3393 // root element (<html>) must to cover entire window
\r
3394 if(!have_parent())
\r
3396 position client_pos;
\r
3397 get_document()->container()->get_client_rect(client_pos);
\r
3398 m_pos.height = std::max(sz.height, client_pos.height) - content_margins_top() - content_margins_bottom();
\r
3399 m_pos.width = std::max(sz.width, client_pos.width) - content_margins_left() - content_margins_right();
\r
3405 void litehtml::html_tag::get_redraw_box(litehtml::position& pos, int x /*= 0*/, int y /*= 0*/)
\r
3409 element::get_redraw_box(pos, x, y);
\r
3411 if(m_overflow == overflow_visible)
\r
3413 for(auto& el : m_children)
\r
3415 if(el->get_element_position() != element_position_fixed)
\r
3417 el->get_redraw_box(pos, x + m_pos.x, y + m_pos.y);
\r
3424 litehtml::element::ptr litehtml::html_tag::find_adjacent_sibling( const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/ )
\r
3427 for(auto& e : m_children)
\r
3429 if(e->get_display() != display_inline_text)
\r
3435 int res = ret->select(selector, apply_pseudo);
\r
3436 if(res != select_no_match)
\r
3440 if(res & select_match_pseudo_class)
\r
3442 *is_pseudo = true;
\r
3445 *is_pseudo = false;
\r
3461 litehtml::element::ptr litehtml::html_tag::find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/)
\r
3463 element::ptr ret = 0;
\r
3464 for(auto& e : m_children)
\r
3466 if(e->get_display() != display_inline_text)
\r
3473 int res = e->select(selector, apply_pseudo);
\r
3474 if(res != select_no_match)
\r
3478 if(res & select_match_pseudo_class)
\r
3480 *is_pseudo = true;
\r
3483 *is_pseudo = false;
\r
3494 bool litehtml::html_tag::is_only_child(const element::ptr& el, bool of_type) const
\r
3496 int child_count = 0;
\r
3497 for(const auto& child : m_children)
\r
3499 if(child->get_display() != display_inline_text)
\r
3501 if( !of_type || (of_type && !t_strcmp(el->get_tagName(), child->get_tagName())) )
\r
3505 if(child_count > 1) break;
\r
3508 if(child_count > 1)
\r
3515 void litehtml::html_tag::update_floats(int dy, const element::ptr &parent)
\r
3517 if(is_floats_holder())
\r
3519 bool reset_cache = false;
\r
3520 for(floated_box::vector::reverse_iterator fb = m_floats_left.rbegin(); fb != m_floats_left.rend(); fb++)
\r
3522 if(fb->el->is_ancestor(parent))
\r
3524 reset_cache = true;
\r
3530 m_cahe_line_left.invalidate();
\r
3532 reset_cache = false;
\r
3533 for(floated_box::vector::reverse_iterator fb = m_floats_right.rbegin(); fb != m_floats_right.rend(); fb++)
\r
3535 if(fb->el->is_ancestor(parent))
\r
3537 reset_cache = true;
\r
3543 m_cahe_line_right.invalidate();
\r
3547 element::ptr el_parent = this->parent();
\r
3550 el_parent->update_floats(dy, parent);
\r
3555 void litehtml::html_tag::remove_before_after()
\r
3557 if(!m_children.empty())
\r
3559 if( !t_strcmp(m_children.front()->get_tagName(), _t("::before")) )
\r
3561 m_children.erase(m_children.begin());
\r
3564 if(!m_children.empty())
\r
3566 if( !t_strcmp(m_children.back()->get_tagName(), _t("::after")) )
\r
3568 m_children.erase(m_children.end() - 1);
\r
3573 litehtml::element::ptr litehtml::html_tag::get_element_before()
\r
3575 if(!m_children.empty())
\r
3577 if( !t_strcmp(m_children.front()->get_tagName(), _t("::before")) )
\r
3579 return m_children.front();
\r
3582 element::ptr el = std::make_shared<el_before>(get_document());
\r
3583 el->parent(shared_from_this());
\r
3584 m_children.insert(m_children.begin(), el);
\r
3588 litehtml::element::ptr litehtml::html_tag::get_element_after()
\r
3590 if(!m_children.empty())
\r
3592 if( !t_strcmp(m_children.back()->get_tagName(), _t("::after")) )
\r
3594 return m_children.back();
\r
3597 element::ptr el = std::make_shared<el_after>(get_document());
\r
3602 void litehtml::html_tag::add_style( const litehtml::style& st )
\r
3604 m_style.combine(st);
\r
3607 bool litehtml::html_tag::have_inline_child() const
\r
3609 if(!m_children.empty())
\r
3611 for(const auto& el : m_children)
\r
3613 if(!el->is_white_space())
\r
3622 void litehtml::html_tag::refresh_styles()
\r
3624 remove_before_after();
\r
3626 for (auto& el : m_children)
\r
3628 if(el->get_display() != display_inline_text)
\r
3630 el->refresh_styles();
\r
3636 for (auto& usel : m_used_styles)
\r
3638 usel->m_used = false;
\r
3640 if(usel->m_selector->is_media_valid())
\r
3642 int apply = select(*usel->m_selector, false);
\r
3644 if(apply != select_no_match)
\r
3646 if(apply & select_match_pseudo_class)
\r
3648 if(select(*usel->m_selector, true))
\r
3650 if(apply & select_match_with_after)
\r
3652 element::ptr el = get_element_after();
\r
3655 el->add_style(*usel->m_selector->m_style);
\r
3657 } else if(apply & select_match_with_before)
\r
3659 element::ptr el = get_element_before();
\r
3662 el->add_style(*usel->m_selector->m_style);
\r
3667 add_style(*usel->m_selector->m_style);
\r
3668 usel->m_used = true;
\r
3671 } else if(apply & select_match_with_after)
\r
3673 element::ptr el = get_element_after();
\r
3676 el->add_style(*usel->m_selector->m_style);
\r
3678 } else if(apply & select_match_with_before)
\r
3680 element::ptr el = get_element_before();
\r
3683 el->add_style(*usel->m_selector->m_style);
\r
3687 add_style(*usel->m_selector->m_style);
\r
3688 usel->m_used = true;
\r
3695 litehtml::element::ptr litehtml::html_tag::get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex)
\r
3697 element::ptr ret = 0;
\r
3699 if(m_overflow > overflow_visible)
\r
3701 if(!m_pos.is_point_inside(x, y))
\r
3707 position pos = m_pos;
\r
3708 pos.x = x - pos.x;
\r
3709 pos.y = y - pos.y;
\r
3711 for(elements_vector::reverse_iterator i = m_children.rbegin(); i != m_children.rend() && !ret; i++)
\r
3713 element::ptr el = (*i);
\r
3715 if(el->is_visible() && el->get_display() != display_inline_text)
\r
3719 case draw_positioned:
\r
3720 if(el->is_positioned() && el->get_zindex() == zindex)
\r
3722 if(el->get_element_position() == element_position_fixed)
\r
3724 ret = el->get_element_by_point(client_x, client_y, client_x, client_y);
\r
3725 if(!ret && (*i)->is_point_inside(client_x, client_y))
\r
3731 ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y);
\r
3732 if(!ret && (*i)->is_point_inside(pos.x, pos.y))
\r
3741 if(!el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
\r
3743 if(el->is_point_inside(pos.x, pos.y))
\r
3750 if(el->get_float() != float_none && !el->is_positioned())
\r
3752 ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y);
\r
3754 if(!ret && (*i)->is_point_inside(pos.x, pos.y))
\r
3761 case draw_inlines:
\r
3762 if(el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
\r
3764 if(el->get_display() == display_inline_block)
\r
3766 ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y);
\r
3769 if(!ret && (*i)->is_point_inside(pos.x, pos.y))
\r
3779 if(el && !el->is_positioned())
\r
3781 if(flag == draw_positioned)
\r
3783 element::ptr child = el->get_child_by_point(pos.x, pos.y, client_x, client_y, flag, zindex);
\r
3790 if( el->get_float() == float_none &&
\r
3791 el->get_display() != display_inline_block)
\r
3793 element::ptr child = el->get_child_by_point(pos.x, pos.y, client_x, client_y, flag, zindex);
\r
3807 litehtml::element::ptr litehtml::html_tag::get_element_by_point(int x, int y, int client_x, int client_y)
\r
3809 if(!is_visible()) return 0;
\r
3813 std::map<int, bool> zindexes;
\r
3815 for(elements_vector::iterator i = m_positioned.begin(); i != m_positioned.end(); i++)
\r
3817 zindexes[(*i)->get_zindex()];
\r
3820 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++)
\r
3822 if(idx->first > 0)
\r
3824 ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first);
\r
3827 if(ret) return ret;
\r
3829 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++)
\r
3831 if(idx->first == 0)
\r
3833 ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first);
\r
3836 if(ret) return ret;
\r
3838 ret = get_child_by_point(x, y, client_x, client_y, draw_inlines, 0);
\r
3839 if(ret) return ret;
\r
3841 ret = get_child_by_point(x, y, client_x, client_y, draw_floats, 0);
\r
3842 if(ret) return ret;
\r
3844 ret = get_child_by_point(x, y, client_x, client_y, draw_block, 0);
\r
3845 if(ret) return ret;
\r
3848 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++)
\r
3850 if(idx->first < 0)
\r
3852 ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first);
\r
3855 if(ret) return ret;
\r
3857 if(m_el_position == element_position_fixed)
\r
3859 if(is_point_inside(client_x, client_y))
\r
3861 ret = shared_from_this();
\r
3865 if(is_point_inside(x, y))
\r
3867 ret = shared_from_this();
\r
3874 const litehtml::background* litehtml::html_tag::get_background(bool own_only)
\r
3878 // return own background with check for empty one
\r
3879 if(m_bg.m_image.empty() && !m_bg.m_color.alpha)
\r
3886 if(m_bg.m_image.empty() && !m_bg.m_color.alpha)
\r
3888 // if this is root element (<html>) try to get background from body
\r
3889 if (!have_parent())
\r
3891 for (const auto& el : m_children)
\r
3893 if( el->is_body() )
\r
3895 // return own body background
\r
3896 return el->get_background(true);
\r
3905 element::ptr el_parent = parent();
\r
3908 if (!el_parent->get_background(true))
\r
3910 // parent of body will draw background for body
\r
3919 int litehtml::html_tag::render_box(int x, int y, int max_width, bool second_pass /*= false*/)
\r
3921 int parent_width = max_width;
\r
3923 calc_outlines(parent_width);
\r
3926 m_pos.move_to(x, y);
\r
3928 m_pos.x += content_margins_left();
\r
3929 m_pos.y += content_margins_top();
\r
3931 int ret_width = 0;
\r
3933 def_value<int> block_width(0);
\r
3935 if (m_display != display_table_cell && !m_css_width.is_predefined())
\r
3937 int w = calc_width(parent_width);
\r
3939 if (m_box_sizing == box_sizing_border_box)
\r
3941 w -= m_padding.width() + m_borders.width();
\r
3943 ret_width = max_width = block_width = w;
\r
3949 max_width -= content_margins_left() + content_margins_right();
\r
3953 // check for max-width (on the first pass only)
\r
3954 if (!m_css_max_width.is_predefined() && !second_pass)
\r
3956 int mw = get_document()->cvt_units(m_css_max_width, m_font_size, parent_width);
\r
3957 if (m_box_sizing == box_sizing_border_box)
\r
3959 mw -= m_padding.left + m_borders.left + m_padding.right + m_borders.right;
\r
3961 if (max_width > mw)
\r
3967 m_floats_left.clear();
\r
3968 m_floats_right.clear();
\r
3970 m_cahe_line_left.invalidate();
\r
3971 m_cahe_line_right.invalidate();
\r
3973 element_position el_position;
\r
3975 int block_height = 0;
\r
3979 if (get_predefined_height(block_height))
\r
3981 m_pos.height = block_height;
\r
3984 white_space ws = get_white_space();
\r
3985 bool skip_spaces = false;
\r
3986 if (ws == white_space_normal ||
\r
3987 ws == white_space_nowrap ||
\r
3988 ws == white_space_pre_line)
\r
3990 skip_spaces = true;
\r
3993 bool was_space = false;
\r
3995 for (auto el : m_children)
\r
3997 // we don't need process absolute and fixed positioned element on the second pass
\r
4000 el_position = el->get_element_position();
\r
4001 if ((el_position == element_position_absolute || el_position == element_position_fixed)) continue;
\r
4004 // skip spaces to make rendering a bit faster
\r
4007 if (el->is_white_space())
\r
4021 was_space = false;
\r
4025 // place element into rendering flow
\r
4026 int rw = place_element(el, max_width);
\r
4027 if (rw > ret_width)
\r
4033 finish_last_box(true);
\r
4035 if (block_width.is_default() && is_inline_box())
\r
4037 m_pos.width = ret_width;
\r
4041 m_pos.width = max_width;
\r
4043 calc_auto_margins(parent_width);
\r
4045 if (!m_boxes.empty())
\r
4047 if (collapse_top_margin())
\r
4049 int old_top = m_margins.top;
\r
4050 m_margins.top = std::max(m_boxes.front()->top_margin(), m_margins.top);
\r
4051 if (m_margins.top != old_top)
\r
4053 update_floats(m_margins.top - old_top, shared_from_this());
\r
4056 if (collapse_bottom_margin())
\r
4058 m_margins.bottom = std::max(m_boxes.back()->bottom_margin(), m_margins.bottom);
\r
4059 m_pos.height = m_boxes.back()->bottom() - m_boxes.back()->bottom_margin();
\r
4063 m_pos.height = m_boxes.back()->bottom();
\r
4067 // add the floats height to the block height
\r
4068 if (is_floats_holder())
\r
4070 int floats_height = get_floats_height();
\r
4071 if (floats_height > m_pos.height)
\r
4073 m_pos.height = floats_height;
\r
4077 // calculate the final position
\r
4079 m_pos.move_to(x, y);
\r
4080 m_pos.x += content_margins_left();
\r
4081 m_pos.y += content_margins_top();
\r
4083 if (get_predefined_height(block_height))
\r
4085 m_pos.height = block_height;
\r
4088 int min_height = 0;
\r
4089 if (!m_css_min_height.is_predefined() && m_css_min_height.units() == css_units_percentage)
\r
4091 element::ptr el_parent = parent();
\r
4094 if (el_parent->get_predefined_height(block_height))
\r
4096 min_height = m_css_min_height.calc_percent(block_height);
\r
4102 min_height = (int)m_css_min_height.val();
\r
4104 if (min_height != 0 && m_box_sizing == box_sizing_border_box)
\r
4106 min_height -= m_padding.top + m_borders.top + m_padding.bottom + m_borders.bottom;
\r
4107 if (min_height < 0) min_height = 0;
\r
4110 if (m_display == display_list_item)
\r
4112 const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0);
\r
4116 css::parse_css_url(list_image, url);
\r
4119 const tchar_t* list_image_baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0);
\r
4120 get_document()->container()->get_image_size(url.c_str(), list_image_baseurl, sz);
\r
4121 if (min_height < sz.height)
\r
4123 min_height = sz.height;
\r
4129 if (min_height > m_pos.height)
\r
4131 m_pos.height = min_height;
\r
4134 int min_width = m_css_min_width.calc_percent(parent_width);
\r
4136 if (min_width != 0 && m_box_sizing == box_sizing_border_box)
\r
4138 min_width -= m_padding.left + m_borders.left + m_padding.right + m_borders.right;
\r
4139 if (min_width < 0) min_width = 0;
\r
4142 if (min_width != 0)
\r
4144 if (min_width > m_pos.width)
\r
4146 m_pos.width = min_width;
\r
4148 if (min_width > ret_width)
\r
4150 ret_width = min_width;
\r
4154 ret_width += content_margins_left() + content_margins_right();
\r
4156 // re-render with new width
\r
4157 if (ret_width < max_width && !second_pass && have_parent())
\r
4159 if (m_display == display_inline_block ||
\r
4160 m_css_width.is_predefined() &&
\r
4161 (m_float != float_none ||
\r
4162 m_display == display_table ||
\r
4163 m_el_position == element_position_absolute ||
\r
4164 m_el_position == element_position_fixed
\r
4168 render(x, y, ret_width, true);
\r
4169 m_pos.width = ret_width - (content_margins_left() + content_margins_right());
\r
4173 if (is_floats_holder() && !second_pass)
\r
4175 for (const auto& fb : m_floats_left)
\r
4177 fb.el->apply_relative_shift(fb.el->parent()->calc_width(m_pos.width));
\r
4185 int litehtml::html_tag::render_table(int x, int y, int max_width, bool second_pass /*= false*/)
\r
4187 if (!m_grid) return 0;
\r
4189 int parent_width = max_width;
\r
4191 calc_outlines(parent_width);
\r
4194 m_pos.move_to(x, y);
\r
4196 m_pos.x += content_margins_left();
\r
4197 m_pos.y += content_margins_top();
\r
4199 def_value<int> block_width(0);
\r
4201 if (!m_css_width.is_predefined())
\r
4203 max_width = block_width = calc_width(parent_width) - m_padding.width() - m_borders.width();
\r
4209 max_width -= content_margins_left() + content_margins_right();
\r
4213 // Calculate table spacing
\r
4214 int table_width_spacing = 0;
\r
4215 if (m_border_collapse == border_collapse_separate)
\r
4217 table_width_spacing = m_border_spacing_x * (m_grid->cols_count() + 1);
\r
4221 table_width_spacing = 0;
\r
4223 if (m_grid->cols_count())
\r
4225 table_width_spacing -= std::min(border_left(), m_grid->column(0).border_left);
\r
4226 table_width_spacing -= std::min(border_right(), m_grid->column(m_grid->cols_count() - 1).border_right);
\r
4229 for (int col = 1; col < m_grid->cols_count(); col++)
\r
4231 table_width_spacing -= std::min(m_grid->column(col).border_left, m_grid->column(col - 1).border_right);
\r
4236 // Calculate the minimum content width (MCW) of each cell: the formatted content may span any number of lines but may not overflow the cell box.
\r
4237 // If the specified 'width' (W) of the cell is greater than MCW, W is the minimum cell width. A value of 'auto' means that MCW is the minimum
\r
4240 // Also, calculate the "maximum" cell width of each cell: formatting the content without breaking lines other than where explicit line breaks occur.
\r
4242 if (m_grid->cols_count() == 1 && !block_width.is_default())
\r
4244 for (int row = 0; row < m_grid->rows_count(); row++)
\r
4246 table_cell* cell = m_grid->cell(0, row);
\r
4247 if (cell && cell->el)
\r
4249 cell->min_width = cell->max_width = cell->el->render(0, 0, max_width - table_width_spacing);
\r
4250 cell->el->m_pos.width = cell->min_width - cell->el->content_margins_left() - cell->el->content_margins_right();
\r
4256 for (int row = 0; row < m_grid->rows_count(); row++)
\r
4258 for (int col = 0; col < m_grid->cols_count(); col++)
\r
4260 table_cell* cell = m_grid->cell(col, row);
\r
4261 if (cell && cell->el)
\r
4263 if (!m_grid->column(col).css_width.is_predefined() && m_grid->column(col).css_width.units() != css_units_percentage)
\r
4265 int css_w = m_grid->column(col).css_width.calc_percent(block_width);
\r
4266 int el_w = cell->el->render(0, 0, css_w);
\r
4267 cell->min_width = cell->max_width = std::max(css_w, el_w);
\r
4268 cell->el->m_pos.width = cell->min_width - cell->el->content_margins_left() - cell->el->content_margins_right();
\r
4272 // calculate minimum content width
\r
4273 cell->min_width = cell->el->render(0, 0, 1);
\r
4274 // calculate maximum content width
\r
4275 cell->max_width = cell->el->render(0, 0, max_width - table_width_spacing);
\r
4282 // For each column, determine a maximum and minimum column width from the cells that span only that column.
\r
4283 // The minimum is that required by the cell with the largest minimum cell width (or the column 'width', whichever is larger).
\r
4284 // The maximum is that required by the cell with the largest maximum cell width (or the column 'width', whichever is larger).
\r
4286 for (int col = 0; col < m_grid->cols_count(); col++)
\r
4288 m_grid->column(col).max_width = 0;
\r
4289 m_grid->column(col).min_width = 0;
\r
4290 for (int row = 0; row < m_grid->rows_count(); row++)
\r
4292 if (m_grid->cell(col, row)->colspan <= 1)
\r
4294 m_grid->column(col).max_width = std::max(m_grid->column(col).max_width, m_grid->cell(col, row)->max_width);
\r
4295 m_grid->column(col).min_width = std::max(m_grid->column(col).min_width, m_grid->cell(col, row)->min_width);
\r
4300 // For each cell that spans more than one column, increase the minimum widths of the columns it spans so that together,
\r
4301 // they are at least as wide as the cell. Do the same for the maximum widths.
\r
4302 // If possible, widen all spanned columns by approximately the same amount.
\r
4304 for (int col = 0; col < m_grid->cols_count(); col++)
\r
4306 for (int row = 0; row < m_grid->rows_count(); row++)
\r
4308 if (m_grid->cell(col, row)->colspan > 1)
\r
4310 int max_total_width = m_grid->column(col).max_width;
\r
4311 int min_total_width = m_grid->column(col).min_width;
\r
4312 for (int col2 = col + 1; col2 < col + m_grid->cell(col, row)->colspan; col2++)
\r
4314 max_total_width += m_grid->column(col2).max_width;
\r
4315 min_total_width += m_grid->column(col2).min_width;
\r
4317 if (min_total_width < m_grid->cell(col, row)->min_width)
\r
4319 m_grid->distribute_min_width(m_grid->cell(col, row)->min_width - min_total_width, col, col + m_grid->cell(col, row)->colspan - 1);
\r
4321 if (max_total_width < m_grid->cell(col, row)->max_width)
\r
4323 m_grid->distribute_max_width(m_grid->cell(col, row)->max_width - max_total_width, col, col + m_grid->cell(col, row)->colspan - 1);
\r
4329 // If the 'table' or 'inline-table' element's 'width' property has a computed value (W) other than 'auto', the used width is the
\r
4330 // greater of W, CAPMIN, and the minimum width required by all the columns plus cell spacing or borders (MIN).
\r
4331 // If the used width is greater than MIN, the extra width should be distributed over the columns.
\r
4333 // If the 'table' or 'inline-table' element has 'width: auto', the used width is the greater of the table's containing block width,
\r
4334 // CAPMIN, and MIN. However, if either CAPMIN or the maximum width required by the columns plus cell spacing or borders (MAX) is
\r
4335 // less than that of the containing block, use max(MAX, CAPMIN).
\r
4338 int table_width = 0;
\r
4339 int min_table_width = 0;
\r
4340 int max_table_width = 0;
\r
4342 if (!block_width.is_default())
\r
4344 table_width = m_grid->calc_table_width(block_width - table_width_spacing, false, min_table_width, max_table_width);
\r
4348 table_width = m_grid->calc_table_width(max_width - table_width_spacing, true, min_table_width, max_table_width);
\r
4351 min_table_width += table_width_spacing;
\r
4352 max_table_width += table_width_spacing;
\r
4353 table_width += table_width_spacing;
\r
4354 m_grid->calc_horizontal_positions(m_borders, m_border_collapse, m_border_spacing_x);
\r
4356 bool row_span_found = false;
\r
4358 // render cells with computed width
\r
4359 for (int row = 0; row < m_grid->rows_count(); row++)
\r
4361 m_grid->row(row).height = 0;
\r
4362 for (int col = 0; col < m_grid->cols_count(); col++)
\r
4364 table_cell* cell = m_grid->cell(col, row);
\r
4367 int span_col = col + cell->colspan - 1;
\r
4368 if (span_col >= m_grid->cols_count())
\r
4370 span_col = m_grid->cols_count() - 1;
\r
4372 int cell_width = m_grid->column(span_col).right - m_grid->column(col).left;
\r
4374 if (cell->el->m_pos.width != cell_width - cell->el->content_margins_left() - cell->el->content_margins_right())
\r
4376 cell->el->render(m_grid->column(col).left, 0, cell_width);
\r
4377 cell->el->m_pos.width = cell_width - cell->el->content_margins_left() - cell->el->content_margins_right();
\r
4381 cell->el->m_pos.x = m_grid->column(col).left + cell->el->content_margins_left();
\r
4384 if (cell->rowspan <= 1)
\r
4386 m_grid->row(row).height = std::max(m_grid->row(row).height, cell->el->height());
\r
4390 row_span_found = true;
\r
4397 if (row_span_found)
\r
4399 for (int col = 0; col < m_grid->cols_count(); col++)
\r
4401 for (int row = 0; row < m_grid->rows_count(); row++)
\r
4403 table_cell* cell = m_grid->cell(col, row);
\r
4406 int span_row = row + cell->rowspan - 1;
\r
4407 if (span_row >= m_grid->rows_count())
\r
4409 span_row = m_grid->rows_count() - 1;
\r
4411 if (span_row != row)
\r
4414 for (int i = row; i <= span_row; i++)
\r
4416 h += m_grid->row(i).height;
\r
4418 if (h < cell->el->height())
\r
4420 m_grid->row(span_row).height += cell->el->height() - h;
\r
4428 // Calculate vertical table spacing
\r
4429 int table_height_spacing = 0;
\r
4430 if (m_border_collapse == border_collapse_separate)
\r
4432 table_height_spacing = m_border_spacing_y * (m_grid->rows_count() + 1);
\r
4436 table_height_spacing = 0;
\r
4438 if (m_grid->rows_count())
\r
4440 table_height_spacing -= std::min(border_top(), m_grid->row(0).border_top);
\r
4441 table_height_spacing -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom);
\r
4444 for (int row = 1; row < m_grid->rows_count(); row++)
\r
4446 table_height_spacing -= std::min(m_grid->row(row).border_top, m_grid->row(row - 1).border_bottom);
\r
4451 // calculate block height
\r
4452 int block_height = 0;
\r
4453 if (get_predefined_height(block_height))
\r
4455 block_height -= m_padding.height() + m_borders.height();
\r
4458 // calculate minimum height from m_css_min_height
\r
4459 int min_height = 0;
\r
4460 if (!m_css_min_height.is_predefined() && m_css_min_height.units() == css_units_percentage)
\r
4462 element::ptr el_parent = parent();
\r
4465 int parent_height = 0;
\r
4466 if (el_parent->get_predefined_height(parent_height))
\r
4468 min_height = m_css_min_height.calc_percent(parent_height);
\r
4474 min_height = (int)m_css_min_height.val();
\r
4477 int extra_row_height = 0;
\r
4478 int minimum_table_height = std::max(block_height, min_height);
\r
4480 m_grid->calc_rows_height(minimum_table_height - table_height_spacing, m_border_spacing_y);
\r
4481 m_grid->calc_vertical_positions(m_borders, m_border_collapse, m_border_spacing_y);
\r
4483 int table_height = 0;
\r
4485 // place cells vertically
\r
4486 for (int col = 0; col < m_grid->cols_count(); col++)
\r
4488 for (int row = 0; row < m_grid->rows_count(); row++)
\r
4490 table_cell* cell = m_grid->cell(col, row);
\r
4493 int span_row = row + cell->rowspan - 1;
\r
4494 if (span_row >= m_grid->rows_count())
\r
4496 span_row = m_grid->rows_count() - 1;
\r
4498 cell->el->m_pos.y = m_grid->row(row).top + cell->el->content_margins_top();
\r
4499 cell->el->m_pos.height = m_grid->row(span_row).bottom - m_grid->row(row).top - cell->el->content_margins_top() - cell->el->content_margins_bottom();
\r
4500 table_height = std::max(table_height, m_grid->row(span_row).bottom);
\r
4501 cell->el->apply_vertical_align();
\r
4506 if (m_border_collapse == border_collapse_collapse)
\r
4508 if (m_grid->rows_count())
\r
4510 table_height -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom);
\r
4515 table_height += m_border_spacing_y;
\r
4518 m_pos.width = table_width;
\r
4520 calc_auto_margins(parent_width);
\r
4522 m_pos.move_to(x, y);
\r
4523 m_pos.x += content_margins_left();
\r
4524 m_pos.y += content_margins_top();
\r
4525 m_pos.width = table_width;
\r
4526 m_pos.height = table_height;
\r
4528 return max_table_width;
\r
4531 void litehtml::html_tag::draw_children_box(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex)
\r
4533 position pos = m_pos;
\r
4537 document::ptr doc = get_document();
\r
4539 if (m_overflow > overflow_visible)
\r
4541 position border_box = pos;
\r
4542 border_box += m_padding;
\r
4543 border_box += m_borders;
\r
4545 border_radiuses bdr_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);
\r
4547 bdr_radius -= m_borders;
\r
4548 bdr_radius -= m_padding;
\r
4550 doc->container()->set_clip(pos, bdr_radius, true, true);
\r
4553 position browser_wnd;
\r
4554 doc->container()->get_client_rect(browser_wnd);
\r
4557 for (auto& item : m_children)
\r
4560 if (el->is_visible())
\r
4564 case draw_positioned:
\r
4565 if (el->is_positioned() && el->get_zindex() == zindex)
\r
4567 if (el->get_element_position() == element_position_fixed)
\r
4569 el->draw(hdc, browser_wnd.x, browser_wnd.y, clip);
\r
4570 el->draw_stacking_context(hdc, browser_wnd.x, browser_wnd.y, clip, true);
\r
4574 el->draw(hdc, pos.x, pos.y, clip);
\r
4575 el->draw_stacking_context(hdc, pos.x, pos.y, clip, true);
\r
4581 if (!el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
\r
4583 el->draw(hdc, pos.x, pos.y, clip);
\r
4587 if (el->get_float() != float_none && !el->is_positioned())
\r
4589 el->draw(hdc, pos.x, pos.y, clip);
\r
4590 el->draw_stacking_context(hdc, pos.x, pos.y, clip, false);
\r
4594 case draw_inlines:
\r
4595 if (el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
\r
4597 el->draw(hdc, pos.x, pos.y, clip);
\r
4598 if (el->get_display() == display_inline_block)
\r
4600 el->draw_stacking_context(hdc, pos.x, pos.y, clip, false);
\r
4611 if (flag == draw_positioned)
\r
4613 if (!el->is_positioned())
\r
4615 el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
\r
4620 if (el->get_float() == float_none &&
\r
4621 el->get_display() != display_inline_block &&
\r
4622 !el->is_positioned())
\r
4624 el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
\r
4631 if (m_overflow > overflow_visible)
\r
4633 doc->container()->del_clip();
\r
4637 void litehtml::html_tag::draw_children_table(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex)
\r
4639 if (!m_grid) return;
\r
4641 position pos = m_pos;
\r
4644 for (int row = 0; row < m_grid->rows_count(); row++)
\r
4646 if (flag == draw_block)
\r
4648 m_grid->row(row).el_row->draw_background(hdc, pos.x, pos.y, clip);
\r
4650 for (int col = 0; col < m_grid->cols_count(); col++)
\r
4652 table_cell* cell = m_grid->cell(col, row);
\r
4655 if (flag == draw_block)
\r
4657 cell->el->draw(hdc, pos.x, pos.y, clip);
\r
4659 cell->el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
\r