#include "html.h" #include "render_item.h" #include "formatting_context.h" void litehtml::formatting_context::add_float(const std::shared_ptr &el, int min_width, int context) { floated_box fb; fb.pos.x = el->left() + m_current_left; fb.pos.y = el->top() + m_current_top; fb.pos.width = el->width(); fb.pos.height = el->height(); fb.float_side = el->src_el()->css().get_float(); fb.clear_floats = el->src_el()->css().get_clear(); fb.el = el; fb.context = context; fb.min_width = min_width; if(fb.float_side == float_left) { if(m_floats_left.empty()) { m_floats_left.push_back(fb); } else { bool inserted = false; for(auto i = m_floats_left.begin(); i != m_floats_left.end(); i++) { if(fb.pos.right() > i->pos.right()) { m_floats_left.insert(i, std::move(fb)); inserted = true; break; } } if(!inserted) { m_floats_left.push_back(std::move(fb)); } } m_cache_line_left.invalidate(); } else if(fb.float_side == float_right) { if(m_floats_right.empty()) { m_floats_right.push_back(std::move(fb)); } else { bool inserted = false; for(auto i = m_floats_right.begin(); i != m_floats_right.end(); i++) { if(fb.pos.left() < i->pos.left()) { m_floats_right.insert(i, std::move(fb)); inserted = true; break; } } if(!inserted) { m_floats_right.push_back(fb); } } m_cache_line_right.invalidate(); } } int litehtml::formatting_context::get_floats_height(element_float el_float) const { int h = m_current_top; for(const auto& fb : m_floats_left) { bool process = false; switch(el_float) { case float_none: process = true; break; case float_left: if (fb.clear_floats == clear_left || fb.clear_floats == clear_both) { process = true; } break; case float_right: if (fb.clear_floats == clear_right || fb.clear_floats == clear_both) { process = true; } break; } if(process) { if(el_float == float_none) { h = std::max(h, fb.pos.bottom()); } else { h = std::max(h, fb.pos.top()); } } } for(const auto& fb : m_floats_right) { int process = false; switch(el_float) { case float_none: process = true; break; case float_left: if (fb.clear_floats == clear_left || fb.clear_floats == clear_both) { process = true; } break; case float_right: if (fb.clear_floats == clear_right || fb.clear_floats == clear_both) { process = true; } break; } if(process) { if(el_float == float_none) { h = std::max(h, fb.pos.bottom()); } else { h = std::max(h, fb.pos.top()); } } } return h - m_current_top; } int litehtml::formatting_context::get_left_floats_height() const { int h = 0; if(!m_floats_left.empty()) { for (const auto& fb : m_floats_left) { h = std::max(h, fb.pos.bottom()); } } return h - m_current_top; } int litehtml::formatting_context::get_right_floats_height() const { int h = 0; if(!m_floats_right.empty()) { for(const auto& fb : m_floats_right) { h = std::max(h, fb.pos.bottom()); } } return h - m_current_top; } int litehtml::formatting_context::get_line_left(int y ) { y += m_current_top; if(m_cache_line_left.is_valid && m_cache_line_left.hash == y) { if(m_cache_line_left.val - m_current_left < 0) { return 0; } return m_cache_line_left.val - m_current_left; } int w = 0; for(const auto& fb : m_floats_left) { if (y >= fb.pos.top() && y < fb.pos.bottom()) { w = std::max(w, fb.pos.right()); if (w < fb.pos.right()) { break; } } } m_cache_line_left.set_value(y, w); w -= m_current_left; if(w < 0) return 0; return w; } int litehtml::formatting_context::get_line_right(int y, int def_right ) { y += m_current_top; def_right += m_current_left; if(m_cache_line_right.is_valid && m_cache_line_right.hash == y) { if(m_cache_line_right.is_default) { return def_right - m_current_left; } else { int w = std::min(m_cache_line_right.val, def_right) - m_current_left; if(w < 0) return 0; return w; } } int w = def_right; m_cache_line_right.is_default = true; for(const auto& fb : m_floats_right) { if(y >= fb.pos.top() && y < fb.pos.bottom()) { w = std::min(w, fb.pos.left()); m_cache_line_right.is_default = false; if(w > fb.pos.left()) { break; } } } m_cache_line_right.set_value(y, w); w -= m_current_left; if(w < 0) return 0; return w; } void litehtml::formatting_context::clear_floats(int context) { auto iter = m_floats_left.begin(); while(iter != m_floats_left.end()) { if(iter->context >= context) { iter = m_floats_left.erase(iter); m_cache_line_left.invalidate(); } else { iter++; } } iter = m_floats_right.begin(); while(iter != m_floats_right.end()) { if(iter->context >= context) { iter = m_floats_right.erase(iter); m_cache_line_right.invalidate(); } else { iter++; } } } int litehtml::formatting_context::get_cleared_top(const std::shared_ptr &el, int line_top) const { switch(el->src_el()->css().get_clear()) { case clear_left: { int fh = get_left_floats_height(); if(fh && fh > line_top) { line_top = fh; } } break; case clear_right: { int fh = get_right_floats_height(); if(fh && fh > line_top) { line_top = fh; } } break; case clear_both: { int fh = get_floats_height(float_none); if(fh && fh > line_top) { line_top = fh; } } break; default: if(el->src_el()->css().get_float() != float_none) { int fh = get_floats_height(el->src_el()->css().get_float()); if(fh && fh > line_top) { line_top = fh; } } break; } return line_top; } int litehtml::formatting_context::find_next_line_top(int top, int width, int def_right ) { top += m_current_top; def_right += m_current_left; int new_top = top; int_vector points; for(const auto& fb : m_floats_left) { if(fb.pos.top() >= top) { if(find(points.begin(), points.end(), fb.pos.top()) == points.end()) { points.push_back(fb.pos.top()); } } if (fb.pos.bottom() >= top) { if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end()) { points.push_back(fb.pos.bottom()); } } } for (const auto& fb : m_floats_right) { if (fb.pos.top() >= top) { if (find(points.begin(), points.end(), fb.pos.top()) == points.end()) { points.push_back(fb.pos.top()); } } if (fb.pos.bottom() >= top) { if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end()) { points.push_back(fb.pos.bottom()); } } } if(!points.empty()) { sort(points.begin(), points.end(), std::less( )); new_top = points.back(); for(auto pt : points) { int pos_left = 0; int pos_right = def_right; get_line_left_right(pt - m_current_top, def_right - m_current_left, pos_left, pos_right); if(pos_right - pos_left >= width) { new_top = pt; break; } } } return new_top - m_current_top; } void litehtml::formatting_context::update_floats(int dy, const std::shared_ptr &parent) { bool reset_cache = false; for(auto fb = m_floats_left.rbegin(); fb != m_floats_left.rend(); fb++) { if(fb->el->src_el()->is_ancestor(parent->src_el())) { reset_cache = true; fb->pos.y += dy; } } if(reset_cache) { m_cache_line_left.invalidate(); } reset_cache = false; for(auto fb = m_floats_right.rbegin(); fb != m_floats_right.rend(); fb++) { if(fb->el->src_el()->is_ancestor(parent->src_el())) { reset_cache = true; fb->pos.y += dy; } } if(reset_cache) { m_cache_line_right.invalidate(); } } void litehtml::formatting_context::apply_relative_shift(const containing_block_context &containing_block_size) { for (const auto& fb : m_floats_left) { fb.el->apply_relative_shift(containing_block_size); } } int litehtml::formatting_context::find_min_left(int y, int context_idx) { y += m_current_top; int min_left = m_current_left; for(const auto& fb : m_floats_left) { if (y >= fb.pos.top() && y < fb.pos.bottom() && fb.context == context_idx) { min_left += fb.min_width; } } if(min_left < m_current_left) return 0; return min_left - m_current_left; } int litehtml::formatting_context::find_min_right(int y, int right, int context_idx) { y += m_current_top; int min_right = right + m_current_left; for(const auto& fb : m_floats_right) { if (y >= fb.pos.top() && y < fb.pos.bottom() && fb.context == context_idx) { min_right -= fb.min_width; } } if(min_right < m_current_left) return 0; return min_right - m_current_left; }