#include "html.h"
#include "render_block.h"
#include "render_inline_context.h"
#include "render_block_context.h"
#include "document.h"
int litehtml::render_item_block::place_float(const std::shared_ptr &el, int top, const containing_block_context &self_size, formatting_context* fmt_ctx)
{
int line_top = fmt_ctx->get_cleared_top(el, top);
int line_left = 0;
int line_right = self_size.render_width;
fmt_ctx->get_line_left_right(line_top, self_size.render_width, line_left, line_right);
int ret_width = 0;
int min_rendered_width = el->render(line_left, line_top, self_size.new_width(line_right), fmt_ctx);
if(min_rendered_width < el->width() && el->src_el()->css().get_width().is_predefined())
{
el->render(line_left, line_top, self_size.new_width(min_rendered_width), fmt_ctx);
}
if (el->src_el()->css().get_float() == float_left)
{
if(el->right() > line_right)
{
line_top = fmt_ctx->find_next_line_top(el->top(), el->width(), self_size.render_width);
el->pos().x = fmt_ctx->get_line_left(line_top) + el->content_offset_left();
el->pos().y = line_top + el->content_offset_top();
}
fmt_ctx->add_float(el, min_rendered_width, self_size.context_idx);
fix_line_width(float_left, self_size, fmt_ctx);
ret_width = fmt_ctx->find_min_left(line_top, self_size.context_idx);
} else if (el->src_el()->css().get_float() == float_right)
{
if(line_left + el->width() > line_right)
{
int new_top = fmt_ctx->find_next_line_top(el->top(), el->width(), self_size.render_width);
el->pos().x = fmt_ctx->get_line_right(new_top, self_size.render_width) - el->width() + el->content_offset_left();
el->pos().y = new_top + el->content_offset_top();
} else
{
el->pos().x = line_right - el->width() + el->content_offset_left();
}
fmt_ctx->add_float(el, min_rendered_width, self_size.context_idx);
fix_line_width(float_right, self_size, fmt_ctx);
line_right = fmt_ctx->find_min_right(line_top, self_size.render_width, self_size.context_idx);
ret_width = self_size.render_width - line_right;
}
return ret_width;
}
std::shared_ptr litehtml::render_item_block::init()
{
std::shared_ptr ret;
// Initialize indexes for list items
if(src_el()->css().get_display() == display_list_item && src_el()->css().get_list_style_type() >= list_style_type_armenian)
{
if (auto p = src_el()->parent())
{
int val = atoi(p->get_attr("start", "1"));
for(const auto &child : p->children())
{
if (child == src_el())
{
src_el()->set_attr("list_index", std::to_string(val).c_str());
break;
}
else if (child->css().get_display() == display_list_item)
val++;
}
}
}
// Split inline blocks with box blocks inside
auto iter = m_children.begin();
while (iter != m_children.end())
{
const auto& el = *iter;
if(el->src_el()->css().get_display() == display_inline && !el->children().empty())
{
auto split_el = el->split_inlines();
if(std::get<0>(split_el))
{
iter = m_children.erase(iter);
iter = m_children.insert(iter, std::get<2>(split_el));
iter = m_children.insert(iter, std::get<1>(split_el));
iter = m_children.insert(iter, std::get<0>(split_el));
std::get<0>(split_el)->parent(shared_from_this());
std::get<1>(split_el)->parent(shared_from_this());
std::get<2>(split_el)->parent(shared_from_this());
continue;
}
}
++iter;
}
bool has_block_level = false;
bool has_inlines = false;
for (const auto& el : m_children)
{
if(!el->src_el()->is_float())
{
if (el->src_el()->is_block_box())
{
has_block_level = true;
} else if (el->src_el()->is_inline())
{
has_inlines = true;
}
}
if(has_block_level && has_inlines)
break;
}
if(has_block_level)
{
ret = std::make_shared(src_el());
ret->parent(parent());
auto doc = src_el()->get_document();
decltype(m_children) new_children;
decltype(m_children) inlines;
bool not_ws_added = false;
for (const auto& el : m_children)
{
if(el->src_el()->is_inline())
{
inlines.push_back(el);
if(!el->src_el()->is_white_space())
not_ws_added = true;
} else
{
if(not_ws_added)
{
auto anon_el = std::make_shared(src_el());
auto anon_ri = std::make_shared(anon_el);
for(const auto& inl : inlines)
{
anon_ri->add_child(inl);
}
not_ws_added = false;
new_children.push_back(anon_ri);
anon_ri->parent(ret);
}
new_children.push_back(el);
el->parent(ret);
inlines.clear();
}
}
if(!inlines.empty() && not_ws_added)
{
auto anon_el = std::make_shared(src_el());
auto anon_ri = std::make_shared(anon_el);
for(const auto& inl : inlines)
{
anon_ri->add_child(inl);
}
new_children.push_back(anon_ri);
anon_ri->parent(ret);
}
ret->children() = new_children;
}
if(!ret)
{
ret = std::make_shared(src_el());
ret->parent(parent());
ret->children() = children();
for (const auto &el: ret->children())
{
el->parent(ret);
}
}
ret->src_el()->add_render(ret);
for(auto& el : ret->children())
{
el = el->init();
}
return ret;
}
int litehtml::render_item_block::_render(int x, int y, const containing_block_context &containing_block_size, formatting_context* fmt_ctx, bool second_pass)
{
containing_block_context self_size = calculate_containing_block_context(containing_block_size);
//*****************************************
// Render content
//*****************************************
int ret_width = _render_content(x, y, second_pass, self_size, fmt_ctx);
//*****************************************
if (src_el()->css().get_display() == display_list_item)
{
if(m_pos.height == 0)
{
m_pos.height = css().get_line_height();
}
}
bool requires_rerender = false; // when true, the second pass for content rendering is required
// Set block width
if(!(containing_block_size.size_mode & containing_block_context::size_mode_content))
{
if(self_size.width.type == containing_block_context::cbc_value_type_absolute)
{
ret_width = m_pos.width = self_size.render_width;
} else
{
m_pos.width = self_size.render_width;
}
} else
{
m_pos.width = ret_width;
if(self_size.width.type == containing_block_context::cbc_value_type_absolute && ret_width > self_size.width)
{
ret_width = self_size.width;
}
}
// Fix width with max-width attribute
if(self_size.max_width.type != containing_block_context::cbc_value_type_none)
{
if(m_pos.width > self_size.max_width)
{
m_pos.width = self_size.max_width;
requires_rerender = true;
}
}
// Fix width with min-width attribute
if(self_size.min_width.type != containing_block_context::cbc_value_type_none)
{
if(m_pos.width < self_size.min_width)
{
m_pos.width = self_size.min_width;
requires_rerender = true;
}
} else if(m_pos.width < 0)
{
m_pos.width = 0;
}
// re-render content with new width if required
if (requires_rerender && !second_pass && !is_root())
{
if(src_el()->is_block_formatting_context())
{
fmt_ctx->clear_floats(-1);
} else
{
fmt_ctx->clear_floats(self_size.context_idx);
}
_render_content(x, y, true, self_size.new_width(m_pos.width), fmt_ctx);
}
// Set block height
if (self_size.height.type != containing_block_context::cbc_value_type_auto &&
!(containing_block_size.size_mode & containing_block_context::size_mode_content))
{
// TODO: Something wrong here
// Percentage height from undefined containing block height is usually <= 0
if(self_size.height.type == containing_block_context::cbc_value_type_percentage)
{
if (self_size.height > 0)
{
m_pos.height = self_size.height;
}
} else
{
m_pos.height = self_size.height;
}
if (src_el()->css().get_box_sizing() == box_sizing_border_box)
{
m_pos.height -= box_sizing_height();
}
} else if (src_el()->is_block_formatting_context())
{
// add the floats' height to the block height
int floats_height = fmt_ctx->get_floats_height();
if (floats_height > m_pos.height)
{
m_pos.height = floats_height;
}
}
if(containing_block_size.size_mode & containing_block_context::size_mode_content)
{
if(self_size.height.type == containing_block_context::cbc_value_type_absolute)
{
if(m_pos.height > self_size.height)
{
m_pos.height = self_size.height;
}
}
}
// Fix height with min-height attribute
if(self_size.min_height.type != containing_block_context::cbc_value_type_none)
{
if(m_pos.height < self_size.min_height)
{
m_pos.height = self_size.min_height;
}
} else if(m_pos.height < 0)
{
m_pos.height = 0;
}
// Fix width with max-width attribute
if(self_size.max_height.type != containing_block_context::cbc_value_type_none)
{
if(m_pos.height > self_size.max_height)
{
m_pos.height = self_size.max_height;
}
}
// calculate the final position
m_pos.move_to(x, y);
m_pos.x += content_offset_left();
m_pos.y += content_offset_top();
if (src_el()->css().get_display() == display_list_item)
{
string list_image = src_el()->css().get_list_style_image();
if (!list_image.empty())
{
size sz;
string list_image_baseurl = src_el()->css().get_list_style_image_baseurl();
src_el()->get_document()->container()->get_image_size(list_image.c_str(), list_image_baseurl.c_str(), sz);
if (m_pos.height < sz.height)
{
m_pos.height = sz.height;
}
}
}
return ret_width + content_offset_width();
}