#include "render_item.h"
#include "document.h"
#include <typeinfo>
#include "document_container.h"
#include "types.h"
litehtml::render_item::render_item(std::shared_ptr<element> _src_el) :
m_element(std::move(_src_el)),
m_skip(false)
{
document::ptr doc = src_el()->get_document();
auto fm = css().get_font_metrics();
m_margins.left = doc->to_pixels(src_el()->css().get_margins().left, fm, 0);
m_margins.right = doc->to_pixels(src_el()->css().get_margins().right, fm, 0);
m_margins.top = doc->to_pixels(src_el()->css().get_margins().top, fm, 0);
m_margins.bottom = doc->to_pixels(src_el()->css().get_margins().bottom, fm, 0);
m_padding.left = doc->to_pixels(src_el()->css().get_padding().left, fm, 0);
m_padding.right = doc->to_pixels(src_el()->css().get_padding().right, fm, 0);
m_padding.top = doc->to_pixels(src_el()->css().get_padding().top, fm, 0);
m_padding.bottom = doc->to_pixels(src_el()->css().get_padding().bottom, fm, 0);
m_borders.left = doc->to_pixels(src_el()->css().get_borders().left.width, fm, 0);
m_borders.right = doc->to_pixels(src_el()->css().get_borders().right.width, fm, 0);
m_borders.top = doc->to_pixels(src_el()->css().get_borders().top.width, fm, 0);
m_borders.bottom = doc->to_pixels(src_el()->css().get_borders().bottom.width, fm, 0);
}
litehtml::pixel_t litehtml::render_item::render(pixel_t x, pixel_t y, const containing_block_context& containing_block_size, formatting_context* fmt_ctx, bool second_pass)
{
pixel_t ret;
calc_outlines(containing_block_size.width);
m_pos.clear();
m_pos.move_to(x, y);
pixel_t content_left = content_offset_left();
pixel_t content_top = content_offset_top();
m_pos.x += content_left;
m_pos.y += content_top;
if(src_el()->is_block_formatting_context() || ! fmt_ctx)
{
formatting_context fmt;
ret = _render(x, y, containing_block_size, &fmt, second_pass);
fmt.apply_relative_shift(containing_block_size);
} else
{
fmt_ctx->push_position(x + content_left, y + content_top);
ret = _render(x, y, containing_block_size, fmt_ctx, second_pass);
fmt_ctx->pop_position(x + content_left, y + content_top);
}
return ret;
}
void litehtml::render_item::calc_outlines( pixel_t parent_width )
{
m_padding.left = m_element->css().get_padding().left.calc_percent(parent_width);
m_padding.right = m_element->css().get_padding().right.calc_percent(parent_width);
m_borders.left = m_element->css().get_borders().left.width.calc_percent(parent_width);
m_borders.right = m_element->css().get_borders().right.width.calc_percent(parent_width);
m_margins.left = m_element->css().get_margins().left.calc_percent(parent_width);
m_margins.right = m_element->css().get_margins().right.calc_percent(parent_width);
m_margins.top = m_element->css().get_margins().top.calc_percent(parent_width);
m_margins.bottom = m_element->css().get_margins().bottom.calc_percent(parent_width);
m_padding.top = m_element->css().get_padding().top.calc_percent(parent_width);
m_padding.bottom = m_element->css().get_padding().bottom.calc_percent(parent_width);
}
litehtml::pixel_t litehtml::render_item::calc_auto_margins(pixel_t parent_width)
{
if ((src_el()->css().get_display() == display_block || src_el()->css().get_display() == display_table) &&
src_el()->css().get_position() != element_position_absolute &&
src_el()->css().get_float() == float_none)
{
if (src_el()->css().get_margins().left.is_predefined() && src_el()->css().get_margins().right.is_predefined())
{
pixel_t el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right;
if (el_width <= parent_width)
{
m_margins.left = (parent_width - el_width) / 2;
m_margins.right = (parent_width - el_width) - m_margins.left;
}
else
{
m_margins.left = 0;
m_margins.right = 0;
}
return m_margins.left;
}
else if (src_el()->css().get_margins().left.is_predefined() && !src_el()->css().get_margins().right.is_predefined())
{
pixel_t el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.right;
m_margins.left = parent_width - el_width;
if (m_margins.left < 0) m_margins.left = 0;
return m_margins.left;
}
else if (!src_el()->css().get_margins().left.is_predefined() && src_el()->css().get_margins().right.is_predefined())
{
pixel_t el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.left;
m_margins.right = parent_width - el_width;
if (m_margins.right < 0) m_margins.right = 0;
}
}
return 0;
}
void litehtml::render_item::apply_relative_shift(const containing_block_context &containing_block_size)
{
if (src_el()->css().get_position() == element_position_relative)
{
css_offsets offsets = src_el()->css().get_offsets();
if (!offsets.left.is_predefined())
{
m_pos.x += offsets.left.calc_percent(containing_block_size.width);
}
else if (!offsets.right.is_predefined())
{
m_pos.x -= offsets.right.calc_percent(containing_block_size.width);
}
if (!offsets.top.is_predefined())
{
m_pos.y += offsets.top.calc_percent(containing_block_size.height);
}
else if (!offsets.bottom.is_predefined())
{
m_pos.y -= offsets.bottom.calc_percent(containing_block_size.height);
}
}
}
std::tuple<
std::shared_ptr<litehtml::render_item>,
std::shared_ptr<litehtml::render_item>,
std::shared_ptr<litehtml::render_item>
> litehtml::render_item::split_inlines()
{
std::tuple<
std::shared_ptr<litehtml::render_item>,
std::shared_ptr<litehtml::render_item>,
std::shared_ptr<litehtml::render_item>
> ret;
for(const auto& child: m_children)
{
if(child->src_el()->is_block_box() && child->src_el()->css().get_float() == float_none)
{
std::get<0>(ret) = clone();
std::get<1>(ret) = child;
std::get<2>(ret) = clone();
std::get<1>(ret)->parent(std::get<0>(ret));
std::get<2>(ret)->parent(std::get<0>(ret));
bool found = false;
for(const auto& ch: m_children)
{
if(ch == child)
{
found = true;
continue;
}
if(!found)
{
std::get<0>(ret)->add_child(ch);
} else
{
std::get<2>(ret)->add_child(ch);
}
}
break;
}
if(!child->children().empty())
{
auto child_split = child->split_inlines();
if(std::get<0>(child_split))
{
std::get<0>(ret) = clone();
std::get<1>(ret) = std::get<1>(child_split);
std::get<2>(ret) = clone();
std::get<2>(ret)->parent(std::get<0>(ret));
bool found = false;
for(const auto& ch: m_children)
{
if(ch == child)
{
found = true;
continue;
}
if(!found)
{
std::get<0>(ret)->add_child(ch);
} else
{
std::get<2>(ret)->add_child(ch);
}
}
std::get<0>(ret)->add_child(std::get<0>(child_split));
std::get<2>(ret)->add_child(std::get<2>(child_split));
break;
}
}
}
return ret;
}
bool litehtml::render_item::fetch_positioned()
{
bool ret = false;
m_positioned.clear();
litehtml::element_position el_pos;
for(auto& el : m_children)
{
el_pos = el->src_el()->css().get_position();
if (el_pos != element_position_static)
{
add_positioned(el);
}
if (!ret && (el_pos == element_position_absolute || el_pos == element_position_fixed))
{
ret = true;
}
if(el->fetch_positioned())
{
ret = true;
}
}
return ret;
}
void litehtml::render_item::render_positioned(render_type rt)
{
position view_port;
src_el()->get_document()->container()->get_viewport(view_port);
element_position el_position;
bool process;
for (auto& el : m_positioned)
{
el_position = el->src_el()->css().get_position();
process = false;
if(el->src_el()->css().get_display() != display_none)
{
if(el_position == element_position_absolute)
{
if(rt != render_fixed_only)
{
process = true;
}
} else if(el_position == element_position_fixed)
{
if(rt != render_no_fixed)
{
process = true;
}
}
}
if(process)
{
containing_block_context containing_block_size;
if(el_position == element_position_fixed || (is_root() && !src_el()->is_positioned()))
{
containing_block_size.height = view_port.height;
containing_block_size.width = view_port.width;
} else
{
containing_block_size.height = m_pos.height + m_padding.height();
containing_block_size.width = m_pos.width + m_padding.width();
}
css_length css_left = el->src_el()->css().get_offsets().left;
css_length css_right = el->src_el()->css().get_offsets().right;
css_length css_top = el->src_el()->css().get_offsets().top;
css_length css_bottom = el->src_el()->css().get_offsets().bottom;
bool need_render = false;
css_length el_width = el->src_el()->css().get_width();
css_length el_height = el->src_el()->css().get_height();
auto fix_height_min_max = [&] (pixel_t height)
{
auto max_height = el->css().get_max_height();
auto min_height = el->css().get_max_height();
if(!max_height.is_predefined())
{
pixel_t max_height_value = max_height.calc_percent(containing_block_size.height);
if(height > max_height_value)
{
height = max_height_value;
}
}
if(!min_height.is_predefined())
{
pixel_t min_height_value = min_height.calc_percent(containing_block_size.height);
if(height < min_height_value)
{
height = min_height_value;
}
}
height += el->content_offset_height();
return height;
};
auto fix_width_min_max = [&] (pixel_t width)
{
auto max_width = el->css().get_max_width();
auto min_width = el->css().get_min_width();
if(!max_width.is_predefined())
{
pixel_t max_width_value = max_width.calc_percent(containing_block_size.width);
if(width > max_width_value)
{
width = max_width_value;
}
}
if(!min_width.is_predefined())
{
pixel_t min_width_value = min_width.calc_percent(containing_block_size.width);
if(width < min_width_value)
{
width = min_width_value;
}
}
width += el->content_offset_width();
return width;
};
pixel_t bottom = 0;
pixel_t top = 0;
pixel_t height = 0;
auto [el_static_offset_x, el_static_offset_y] = element_static_offset(el);
pixel_t el_static_x = el->m_pos.x + el_static_offset_x;
pixel_t el_static_y = el->m_pos.y + el_static_offset_y;
if(css_top.is_predefined() && !css_bottom.is_predefined() && el_height.is_predefined())
{
if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0;
if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0;
height = el->height();
bottom = css_bottom.calc_percent(containing_block_size.height);
top = containing_block_size.height - height - bottom;
} else if(css_top.is_predefined() && css_bottom.is_predefined() && !el_height.is_predefined())
{
if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0;
if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0;
top = el_static_y - el->content_offset_top();
height = fix_height_min_max(el_height.calc_percent(containing_block_size.height));
} else if(!css_top.is_predefined() && css_bottom.is_predefined() && el_height.is_predefined())
{
if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0;
if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0;
height = el->height();
top = css_top.calc_percent(containing_block_size.height);
} else if(css_top.is_predefined() && !css_bottom.is_predefined() && !el_height.is_predefined())
{
if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0;
if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0;
height = fix_height_min_max(el_height.calc_percent(containing_block_size.height));
bottom = css_bottom.calc_percent(containing_block_size.height);
top = containing_block_size.height - height - bottom;
} else if(!css_top.is_predefined() && !css_bottom.is_predefined() && el_height.is_predefined())
{
if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0;
if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0;
bottom = css_bottom.calc_percent(containing_block_size.height);
top = css_top.calc_percent(containing_block_size.height);
if(el->src_el()->is_replaced())
{
height = el->height() - el->content_offset_height();
pixel_t reminded = (containing_block_size.height - top - bottom) - height - el->content_offset_height();
if(reminded > 0)
{
int divider = 0;
if (el->css().get_margins().top.is_predefined()) divider++;
if (el->css().get_margins().bottom.is_predefined()) divider++;
if (divider != 0)
{
if (el->css().get_margins().top.is_predefined()) el->m_margins.top = reminded / divider;
if (el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = reminded / divider;
}
}
height += el->content_offset_height();
} else
{
height = containing_block_size.height - top - bottom;
}
if(!el->css().get_max_height().is_predefined())
{
pixel_t max_height = el->css().get_max_height().calc_percent(containing_block_size.height);
if(height - el->content_offset_height() > max_height)
{
pixel_t reminded = height - el->content_offset_height() - max_height;
height = max_height;
int divider = 0;
if(el->css().get_margins().top.is_predefined()) divider++;
if(el->css().get_margins().bottom.is_predefined()) divider++;
if(divider != 0)
{
if(el->css().get_margins().top.is_predefined()) el->m_margins.top = reminded / divider;
if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = reminded / divider;
}
height += el->content_offset_height();
}
}
} else if(!css_top.is_predefined() && css_bottom.is_predefined() && !el_height.is_predefined())
{
if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0;
if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0;
height = fix_height_min_max(el_height.calc_percent(containing_block_size.height));
top = css_top.calc_percent(containing_block_size.height);
} else if(css_top.is_predefined() && css_bottom.is_predefined() && el_height.is_predefined())
{
if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0;
if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0;
height = el->height();
top = el_static_y - el->content_offset_top();
} else
{
height = fix_height_min_max(el_height.calc_percent(containing_block_size.height));
top = css_top.calc_percent(containing_block_size.height);
bottom = css_bottom.calc_percent(containing_block_size.height);
pixel_t remained = containing_block_size.height - height - top - bottom;
if(el->css().get_margins().top.is_predefined() && el->css().get_margins().bottom.is_predefined())
{
el->m_margins.top = el->m_margins.bottom = remained / 2;
height += el->m_margins.top + el->m_margins.bottom;
} else
{
if(el->css().get_margins().top.is_predefined())
{
el->m_margins.top = remained;
height += el->m_margins.top;
}
if(el->css().get_margins().bottom.is_predefined())
{
el->m_margins.bottom = remained;
height += el->m_margins.bottom;
}
}
}
el->m_pos.y = top + el->content_offset_top();
if(el->m_pos.height != height - el->content_offset_height())
{
el->m_pos.height = height - el->content_offset_height();
need_render = true;
}
pixel_t right = 0;
pixel_t left = 0;
pixel_t width = 0;
if(css_left.is_predefined() && !css_right.is_predefined() && el_width.is_predefined())
{
if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0;
if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0;
width = el->width();
right = css_right.calc_percent(containing_block_size.width);
left = containing_block_size.width - width - right;
} else if(css_left.is_predefined() && css_right.is_predefined() && !el_width.is_predefined())
{
if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0;
if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0;
left = el_static_x - el->content_offset_left();
width = fix_width_min_max(el_width.calc_percent(containing_block_size.width));
} else if(!css_left.is_predefined() && css_right.is_predefined() && el_width.is_predefined())
{
if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0;
if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0;
width = el->width();
left = css_left.calc_percent(containing_block_size.width);
} else if(css_left.is_predefined() && !css_right.is_predefined() && !el_width.is_predefined())
{
if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0;
if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0;
right = css_right.calc_percent(containing_block_size.width);
width = fix_width_min_max(el_width.calc_percent(containing_block_size.width));
left = containing_block_size.width - right - width;
} else if(!css_left.is_predefined() && !css_right.is_predefined() && el_width.is_predefined())
{
if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0;
if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0;
left = css_left.calc_percent(containing_block_size.width);
right = css_right.calc_percent(containing_block_size.width);
if(el->src_el()->is_replaced())
{
width = el->width() - el->content_offset_width();
pixel_t reminded = (containing_block_size.width - left - right) - width - el->content_offset_width();
if(reminded != 0)
{
int divider = 0;
if (el->css().get_margins().left.is_predefined()) divider++;
if (el->css().get_margins().right.is_predefined()) divider++;
if (divider != 0)
{
if (el->css().get_margins().left.is_predefined()) el->m_margins.left = reminded / divider;
if (el->css().get_margins().right.is_predefined()) el->m_margins.right = reminded / divider;
}
}
width += el->content_offset_width();
} else
{
width = containing_block_size.width - left - right;
}
if(!el->css().get_max_width().is_predefined())
{
pixel_t max_width = el->css().get_max_width().calc_percent(containing_block_size.height);
if(width - el->content_offset_width() > max_width)
{
pixel_t reminded = width - el->content_offset_width() - max_width;
width = max_width;
int divider = 0;
if(el->css().get_margins().left.is_predefined()) divider++;
if(el->css().get_margins().right.is_predefined()) divider++;
if(divider != 0)
{
if(el->css().get_margins().left.is_predefined()) el->m_margins.left = reminded / divider;
if(el->css().get_margins().right.is_predefined()) el->m_margins.right = reminded / divider;
}
width += el->content_offset_width();
}
}
} else if(!css_left.is_predefined() && css_right.is_predefined() && !el_width.is_predefined())
{
if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0;
if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0;
left = css_left.calc_percent(containing_block_size.width);
width = fix_width_min_max(el_width.calc_percent(containing_block_size.width));
} else if(css_left.is_predefined() && css_right.is_predefined() && el_width.is_predefined())
{
if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0;
if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0;
width = el->width();
left = el_static_x - el->content_offset_left();
} else
{
width = fix_width_min_max(el_width.calc_percent(containing_block_size.width));
left = css_left.calc_percent(containing_block_size.width);
right = css_right.calc_percent(containing_block_size.width);
pixel_t remained = containing_block_size.width - width - left - right;
if(el->css().get_margins().left.is_predefined() && el->css().get_margins().right.is_predefined())
{
el->m_margins.left = el->m_margins.right = remained / 2;
if(el->m_margins.left < 0)
{
el->m_margins.left = 0;
el->m_margins.right = remained;
}
width += el->m_margins.left + el->m_margins.right;
} else
{
if(el->css().get_margins().left.is_predefined())
{
el->m_margins.left = remained;
width += el->m_margins.left;
}
if(el->css().get_margins().right.is_predefined())
{
el->m_margins.right = remained;
width += el->m_margins.right;
}
}
}
el->m_pos.x = left + el->content_offset_left();
if(el->m_pos.width != width - el->content_offset_width())
{
el->m_pos.width = width - el->content_offset_width();
need_render = true;
}
if(el_position != element_position_fixed)
{
el->m_pos.x -= el_static_offset_x;
el->m_pos.y -= el_static_offset_y;
}
if(need_render)
{
position pos = el->m_pos;
el->render(el->left(), el->top(), containing_block_size.new_width(el->width()), nullptr, true);
el->m_pos = pos;
}
if(el_position == element_position_fixed)
{
position fixed_pos = el->pos();
el->get_redraw_box(fixed_pos);
src_el()->get_document()->add_fixed_box(fixed_pos);
}
}
el->render_positioned();
}
if(!m_positioned.empty())
{
std::stable_sort(m_positioned.begin(), m_positioned.end(), [](const std::shared_ptr<render_item>& Left, const std::shared_ptr<render_item>& Right)
{
return (Left->src_el()->css().get_z_index() < Right->src_el()->css().get_z_index());
});
}
}
void litehtml::render_item::add_positioned(const std::shared_ptr<litehtml::render_item> &el)
{
if (src_el()->css().get_position() != element_position_static || is_root())
{
m_positioned.push_back(el);
} else
{
auto el_parent = parent();
if (el_parent)
{
el_parent->add_positioned(el);
}
}
}
void litehtml::render_item::get_redraw_box(litehtml::position& pos, pixel_t x , pixel_t y )
{
if(is_visible())
{
pixel_t p_left = std::min(pos.left(), x + m_pos.left() - m_padding.left - m_borders.left);
pixel_t p_right = std::max(pos.right(), x + m_pos.right() + m_padding.left + m_borders.left);
pixel_t p_top = std::min(pos.top(), y + m_pos.top() - m_padding.top - m_borders.top);
pixel_t p_bottom = std::max(pos.bottom(), y + m_pos.bottom() + m_padding.bottom + m_borders.bottom);
pos.x = p_left;
pos.y = p_top;
pos.width = p_right - p_left;
pos.height = p_bottom - p_top;
if(src_el()->css().get_overflow() == overflow_visible)
{
for(auto& el : m_children)
{
if(el->src_el()->css().get_position() != element_position_fixed)
{
el->get_redraw_box(pos, x + m_pos.x, y + m_pos.y);
}
}
}
}
}
void litehtml::render_item::calc_document_size(litehtml::size& sz, pixel_t x , pixel_t y )
{
if(css().get_display() != display_inline && css().get_display() != display_table_row)
{
if(is_visible())
{
if(src_el()->css().get_position() != element_position_fixed)
{
sz.width = std::max(sz.width, x + right());
sz.height = std::max(sz.height, y + bottom());
}
if(is_one_of(src_el()->css().get_overflow(), overflow_scroll, overflow_auto))
{
size child_size;
for(const auto& el : m_children)
{
el->calc_document_size(child_size, 0, 0);
}
if(!m_scroll_view)
{
m_scroll_view = std::make_shared<scroll_view>(m_pos, child_size);
} else
{
m_scroll_view->set(m_pos, child_size);
}
} else
{
if(src_el()->css().get_overflow() == overflow_visible && src_el()->css().get_display() != display_table)
{
for(const auto& el : m_children)
{
el->calc_document_size(sz, x + m_pos.x, y + m_pos.y);
}
} else
{
size child_size;
for(const auto& el : m_children)
{
el->calc_document_size(child_size, 0, 0);
}
}
}
}
} else
{
position::vector boxes;
get_inline_boxes(boxes);
for(const auto& box : boxes)
{
sz.width = std::max(sz.width, x + box.x + box.width);
sz.height = std::max(sz.height, y + box.y + box.height);
}
}
}
void litehtml::render_item::draw_stacking_context( uint_ptr hdc, pixel_t x, pixel_t y, const position* clip, bool with_positioned )
{
if(!is_visible()) return;
std::map<int, bool> z_indexes;
if(with_positioned)
{
for(const auto& idx : m_positioned)
{
z_indexes[idx->src_el()->css().get_z_index()];
}
for(const auto& idx : z_indexes)
{
if(idx.first < 0)
{
draw_children(hdc, x, y, clip, draw_positioned, idx.first);
}
}
}
draw_children(hdc, x, y, clip, draw_block, 0);
draw_children(hdc, x, y, clip, draw_floats, 0);
draw_children(hdc, x, y, clip, draw_inlines, 0);
if(with_positioned)
{
for(auto& z_index : z_indexes)
{
if(z_index.first == 0)
{
draw_children(hdc, x, y, clip, draw_positioned, z_index.first);
}
}
for(auto& z_index : z_indexes)
{
if(z_index.first > 0)
{
draw_children(hdc, x, y, clip, draw_positioned, z_index.first);
}
}
}
}
void litehtml::render_item::draw_children(uint_ptr hdc, pixel_t x, pixel_t y, const position* clip, draw_flag flag, int zindex)
{
position pos = m_pos;
pos.x += x - get_scroll_left();
pos.y += y - get_scroll_top();
document::ptr doc = src_el()->get_document();
if (src_el()->css().get_overflow() > overflow_visible)
{
if(src_el()->css().get_display() != display_inline)
{
position clip_box = m_pos;
clip_box.x += x;
clip_box.y += y;
position border_box = clip_box;
border_box += m_padding;
border_box += m_borders;
border_radiuses bdr_radius = src_el()->css().get_borders().radius.calc_percents(border_box.width,
border_box.height);
bdr_radius -= m_borders;
bdr_radius -= m_padding;
doc->container()->set_clip(clip_box, bdr_radius);
}
}
for (const auto& el : m_children)
{
if (el->is_visible())
{
bool process = true;
switch (flag)
{
case draw_positioned:
if (el->src_el()->is_positioned() && el->src_el()->css().get_z_index() == zindex)
{
if (el->src_el()->css().get_position() == element_position_fixed)
{
el->src_el()->draw(hdc, 0, 0, clip, el);
el->draw_stacking_context(hdc, 0, 0, clip, true);
}
else
{
el->src_el()->draw(hdc, pos.x, pos.y, clip, el);
el->draw_stacking_context(hdc, pos.x, pos.y, clip, true);
}
process = false;
}
break;
case draw_block:
if (!el->src_el()->is_inline() && el->src_el()->css().get_float() == float_none && !el->src_el()->is_positioned())
{
el->src_el()->draw(hdc, pos.x, pos.y, clip, el);
}
break;
case draw_floats:
if (el->src_el()->css().get_float() != float_none && !el->src_el()->is_positioned())
{
el->src_el()->draw(hdc, pos.x, pos.y, clip, el);
el->draw_stacking_context(hdc, pos.x, pos.y, clip, false);
process = false;
}
break;
case draw_inlines:
if (el->src_el()->is_inline() && el->src_el()->css().get_float() == float_none && !el->src_el()->is_positioned())
{
el->src_el()->draw(hdc, pos.x, pos.y, clip, el);
if (el->src_el()->css().get_display() == display_inline_block || el->src_el()->css().get_display() == display_inline_flex)
{
el->draw_stacking_context(hdc, pos.x, pos.y, clip, false);
process = false;
}
}
break;
default:
break;
}
if (process)
{
if (flag == draw_positioned)
{
if (!el->src_el()->is_positioned())
{
el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
}
}
else
{
if (el->src_el()->css().get_float() == float_none &&
el->src_el()->css().get_display() != display_inline_block &&
!el->src_el()->is_positioned())
{
el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
}
}
}
}
}
if (src_el()->css().get_overflow() > overflow_visible)
{
doc->container()->del_clip();
}
}
std::shared_ptr<litehtml::element> litehtml::render_item::get_child_by_point(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, draw_flag flag, int zindex, const std::function<bool(const std::shared_ptr<render_item>&)>& check)
{
if(src_el()->css().get_overflow() > overflow_visible)
{
if(!m_pos.is_point_inside(x, y))
{
return nullptr;
}
}
element::ptr ret = nullptr;
position el_pos = m_pos;
el_pos.x = x - el_pos.x + get_scroll_left();
el_pos.y = y - el_pos.y + get_scroll_top();
for(auto i = m_children.crbegin(); i != m_children.crend() && !ret; std::advance(i, 1))
{
const auto& el = *i;
if(el->is_visible() && el->src_el()->css().get_display() != display_inline_text)
{
bool process = true;
switch(flag)
{
case draw_positioned:
if(el->src_el()->is_positioned() && el->src_el()->css().get_z_index() == zindex)
{
if(el->src_el()->css().get_position() == element_position_fixed)
{
ret = el->get_element_by_point(client_x, client_y, client_x, client_y, check);
if(!ret && el->is_point_inside(client_x, client_y))
{
if (!check || check(el))
{
ret = el->src_el();
}
}
} else
{
ret = el->get_element_by_point(el_pos.x, el_pos.y, client_x, client_y, check);
if(!ret && el->is_point_inside(el_pos.x, el_pos.y))
{
if (!check || check(el))
{
ret = el->src_el();
}
}
}
process = false;
}
break;
case draw_block:
if(!el->src_el()->is_inline() && el->src_el()->css().get_float() == float_none && !el->src_el()->is_positioned())
{
ret = el->get_child_by_point(el_pos.x, el_pos.y, client_x, client_y, flag, zindex, check);
if(!ret && el->is_point_inside(el_pos.x, el_pos.y))
{
if (!check || check(el))
{
ret = el->src_el();
}
}
process = false;
}
break;
case draw_floats:
if(el->src_el()->css().get_float() != float_none && !el->src_el()->is_positioned())
{
ret = el->get_element_by_point(el_pos.x, el_pos.y, client_x, client_y, check);
if(!ret && el->is_point_inside(el_pos.x, el_pos.y))
{
ret = el->src_el();
}
process = false;
}
break;
case draw_inlines:
if(el->src_el()->is_inline() && el->src_el()->css().get_float() == float_none && !el->src_el()->is_positioned())
{
if(el->src_el()->css().get_display() == display_inline_block ||
el->src_el()->css().get_display() == display_inline_table ||
el->src_el()->css().get_display() == display_inline_flex)
{
ret = el->get_element_by_point(el_pos.x, el_pos.y, client_x, client_y, check);
process = false;
}
if(!ret && el->is_point_inside(el_pos.x, el_pos.y))
{
if (!check || check(el))
{
ret = el->src_el();
}
}
}
break;
default:
break;
}
if(process && !el->src_el()->is_positioned())
{
if(flag == draw_positioned)
{
element::ptr child = el->get_child_by_point(el_pos.x, el_pos.y, client_x, client_y, flag, zindex, check);
if(child)
{
if(!check)
{
ret = child;
} else
{
child->run_on_renderers([&check, &ret, &child](const std::shared_ptr<render_item>& ri) -> bool {
if(check(ri))
{
ret = child;
return false;
}
return true;
});
}
}
} else
{
if( el->src_el()->css().get_float() == float_none &&
el->src_el()->css().get_display() != display_inline_block && el->src_el()->css().get_display() != display_inline_flex)
{
element::ptr child = el->get_child_by_point(el_pos.x, el_pos.y, client_x, client_y, flag, zindex, check);
if(child)
{
if(!check)
{
ret = child;
} else
{
child->run_on_renderers([&check, &ret, &child](const std::shared_ptr<render_item>& ri) -> bool {
if(check(ri))
{
ret = child;
return false;
}
return true;
});
}
}
}
}
}
}
}
return ret;
}
std::shared_ptr<litehtml::element> litehtml::render_item::get_element_by_point(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, const std::function<bool(const std::shared_ptr<render_item>&)>& check)
{
if(!is_visible()) return nullptr;
element::ptr ret;
std::map<int, bool> z_indexes;
for(const auto& i : m_positioned)
{
z_indexes[i->src_el()->css().get_z_index()];
}
for(auto iter = z_indexes.rbegin(); iter != z_indexes.rend(); ++iter)
{
if(iter->first > 0)
{
ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, iter->first, check);
if(ret) return ret;
}
}
for(const auto& z_index : z_indexes)
{
if(z_index.first == 0)
{
ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, z_index.first, check);
if(ret) return ret;
}
}
ret = get_child_by_point(x, y, client_x, client_y, draw_inlines, 0, check);
if(ret) return ret;
ret = get_child_by_point(x, y, client_x, client_y, draw_floats, 0, check);
if(ret) return ret;
ret = get_child_by_point(x, y, client_x, client_y, draw_block, 0,check);
if(ret) return ret;
for(auto iter = z_indexes.rbegin(); iter != z_indexes.rend(); ++iter)
{
if(iter->first < 0)
{
ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, iter->first, check);
if(ret) return ret;
}
}
if(src_el()->css().get_position() == element_position_fixed)
{
if(is_point_inside(client_x, client_y))
{
if (!check || check(this->shared_from_this()))
{
ret = src_el();
}
}
} else
{
if(is_point_inside(x, y))
{
if (!check || check(this->shared_from_this()))
{
ret = src_el();
}
}
}
return ret;
}
bool litehtml::render_item::is_point_inside( pixel_t x, pixel_t y ) const
{
if(src_el()->css().get_display() != display_inline && src_el()->css().get_display() != display_table_row)
{
position pos = m_pos;
pos += m_padding;
pos += m_borders;
return pos.is_point_inside(x, y);
} else
{
position::vector boxes;
get_inline_boxes(boxes);
for(auto & box : boxes)
{
if(box.is_point_inside(x, y))
{
return true;
}
}
}
return false;
}
void litehtml::render_item::get_rendering_boxes( position::vector& redraw_boxes) const
{
if(src_el()->css().get_display() == display_inline || src_el()->css().get_display() == display_table_row)
{
get_inline_boxes(redraw_boxes);
for(auto& box : redraw_boxes)
{
scroll_box(box);
}
} else
{
position pos = m_pos;
pos += m_padding;
pos += m_borders;
redraw_boxes.push_back(pos);
}
if(src_el()->css().get_position() != element_position_fixed)
{
auto cur_el = parent();
pixel_t add_x = 0;
pixel_t add_y = 0;
while(cur_el)
{
if(cur_el->css().get_position() == element_position_fixed)
{
position view_port;
src_el()->get_document()->container()->get_viewport(view_port);
add_x += cur_el->m_pos.x + view_port.left() - cur_el->get_scroll_left();
add_y += cur_el->m_pos.y + view_port.top() - cur_el->get_scroll_top();
break;
}
add_x += cur_el->m_pos.x - cur_el->get_scroll_left();
add_y += cur_el->m_pos.y - cur_el->get_scroll_top();
cur_el = cur_el->parent();
}
for(auto& box : redraw_boxes)
{
box.x += add_x;
box.y += add_y;
}
} else
{
position view_port;
src_el()->get_document()->container()->get_viewport(view_port);
for(auto& box : redraw_boxes)
{
box.x += view_port.left();
box.y += view_port.top();
}
}
}
void litehtml::render_item::dump(litehtml::dumper& cout)
{
cout.begin_node(src_el()->dump_get_name() + "{" + string(typeid(*this).name()) + "}");
auto attrs = src_el()->dump_get_attrs();
if(!attrs.empty())
{
cout.begin_attrs_group("attributes");
for (const auto &attr: attrs)
{
cout.add_attr(std::get<0>(attr), std::get<1>(attr));
}
cout.end_attrs_group();
}
if(!m_children.empty())
{
cout.begin_attrs_group("children");
for (const auto &el: m_children)
{
el->dump(cout);
}
cout.end_attrs_group();
}
cout.end_node();
}
litehtml::position litehtml::render_item::get_placement() const
{
litehtml::position pos = m_pos;
if(css().get_position() == element_position_fixed)
{
position view_port;
src_el()->get_document()->container()->get_viewport(view_port);
pos.x += view_port.left();
pos.y += view_port.top();
return pos;
}
auto cur_el = parent();
while(cur_el)
{
auto p = cur_el->calc_placement();
pos.x += p.x;
pos.y += p.y;
if(cur_el->css().get_position() == element_position_fixed)
{
position view_port;
src_el()->get_document()->container()->get_viewport(view_port);
pos.x += view_port.left();
pos.y += view_port.top();
return pos;
}
cur_el = cur_el->parent();
}
return pos;
}
std::shared_ptr<litehtml::render_item> litehtml::render_item::init()
{
src_el()->add_render(shared_from_this());
for(auto& el : children())
{
el = el->init();
}
return shared_from_this();
}
void litehtml::render_item::calc_cb_length(const css_length& len, pixel_t percent_base, containing_block_context::typed_pixel& out_value) const
{
if (!len.is_predefined())
{
if(len.units() == litehtml::css_units_percentage)
{
out_value.value = len.calc_percent(percent_base);
out_value.type = litehtml::containing_block_context::cbc_value_type_percentage;
} else
{
out_value.value = src_el()->get_document()->to_pixels(len, css().get_font_metrics(), 0);
out_value.type = containing_block_context::cbc_value_type_absolute;
}
}
}
litehtml::containing_block_context litehtml::render_item::calculate_containing_block_context(const containing_block_context& cb_context)
{
containing_block_context ret;
ret.context_idx = cb_context.context_idx + 1;
ret.width.value = ret.max_width.value = cb_context.width.value - content_offset_width();
if(src_el()->css().get_position() != element_position_absolute && src_el()->css().get_position() != element_position_fixed)
{
ret.height.value = cb_context.height.value - content_offset_height();
}
if (src_el()->css().get_display() != display_table_cell)
{
auto par = parent();
if(cb_context.size_mode & containing_block_context::size_mode_exact_width)
{
ret.width.value = cb_context.width;
ret.width.type = containing_block_context::cbc_value_type_absolute;
} else
{
auto *width = &css().get_width();
if(par && (par->css().get_display() == display_flex || par->css().get_display() == display_inline_flex))
{
if(!css().get_flex_basis().is_predefined() && css().get_flex_basis().val() >= 0)
{
if(par->css().get_flex_direction() == flex_direction_row || par->css().get_flex_direction() == flex_direction_row_reverse)
{
ret.width.type = containing_block_context::cbc_value_type_auto;
ret.width.value = 0;
width = nullptr;
}
}
}
if(width)
{
calc_cb_length(*width, cb_context.width, ret.width);
}
}
if(cb_context.size_mode & containing_block_context::size_mode_exact_height)
{
ret.height.value = cb_context.height;
ret.height.type = containing_block_context::cbc_value_type_absolute;
} else
{
auto *height = &css().get_height();
if(par && (par->css().get_display() == display_flex || par->css().get_display() == display_inline_flex))
{
if(!css().get_flex_basis().is_predefined() && css().get_flex_basis().val() >= 0)
{
if(par->css().get_flex_direction() == flex_direction_column || par->css().get_flex_direction() == flex_direction_column_reverse)
{
ret.height.type = containing_block_context::cbc_value_type_auto;
ret.height.value = 0;
height = nullptr;
}
}
}
if(height)
{
calc_cb_length(*height, cb_context.height, ret.height);
}
}
if (ret.width.type != containing_block_context::cbc_value_type_auto && (src_el()->css().get_display() == display_table || src_el()->is_root()))
{
ret.width.value -= content_offset_width();
}
if (ret.height.type != containing_block_context::cbc_value_type_auto && (src_el()->css().get_display() == display_table || src_el()->is_root()))
{
ret.height.value -= content_offset_height();
}
}
ret.render_width = ret.width;
calc_cb_length(src_el()->css().get_min_width(), cb_context.width, ret.min_width);
calc_cb_length(src_el()->css().get_max_width(), cb_context.width, ret.max_width);
calc_cb_length(src_el()->css().get_min_height(), cb_context.height, ret.min_height);
calc_cb_length(src_el()->css().get_max_height(), cb_context.height, ret.max_height);
if(ret.width.type != containing_block_context::cbc_value_type_auto)
{
ret.render_width = ret.width - box_sizing_width();
}
if(ret.min_width.type != containing_block_context::cbc_value_type_none)
{
ret.min_width.value -= box_sizing_width();
}
if(ret.max_width.type != containing_block_context::cbc_value_type_none)
{
ret.max_width.value -= box_sizing_width();
}
if(ret.min_height.type != containing_block_context::cbc_value_type_none)
{
ret.min_height.value -= box_sizing_height();
}
if(ret.max_height.type != containing_block_context::cbc_value_type_none)
{
ret.max_height.value -= box_sizing_height();
}
return ret;
}
std::tuple<litehtml::pixel_t, litehtml::pixel_t> litehtml::render_item::element_static_offset(const std::shared_ptr<litehtml::render_item>& el)
{
pixel_t offset_x = 0;
pixel_t offset_y = 0;
auto cur_el = el->parent();
auto this_el = el->css().get_position() != element_position_fixed ? shared_from_this() : src_el()->get_document()->root_render();
while(cur_el && cur_el != this_el)
{
offset_x += cur_el->m_pos.x;
offset_y += cur_el->m_pos.y;
cur_el = cur_el->parent();
}
if(el->css().get_position() == element_position_fixed || (is_root() && !src_el()->is_positioned()))
{
offset_x += this_el->m_pos.x;
offset_y += this_el->m_pos.y;
} else
{
offset_x += m_padding.left;
offset_y += m_padding.top;
}
return {offset_x, offset_y};
}
void litehtml::render_item::y_shift(pixel_t delta)
{
m_pos.y += delta;
}