point TextBox::reflowText(LayoutEngine& eng, const Dimensions& containing, const point& current_cursor) { // clear old data so we can re-calculate it. // XXX Future note. // unless the text has changed or the width of the containing box changes we shouldn't // have to recalculate this. lines_.clear(); point cursor = current_cursor; FixedPoint y1 = cursor.y + getOffset().y; // XXX if padding left/border left applies should reduce width and move cursor position if isFirstInlineChild() is set. // Simlarly the last line width should be reduced by padding right/border right. FixedPoint width = eng.getWidthAtPosition(y1, y1 + getLineHeight(), containing.content_.width) - cursor.x + eng.getXAtPosition(y1, y1 + getLineHeight()); Text::iterator last_it = txt_->begin(); Text::iterator it = last_it; bool done = false; while(it != txt_->end()) { LinePtr line = txt_->reflowText(it, width, getStyleNode()); if(line != nullptr && !line->line.empty()) { // is the line larger than available space and are there floats present? FixedPoint last_x = line->line.back().advance.back().x; if(last_x > width && eng.hasFloatsAtPosition(y1, y1 + getLineHeight())) { cursor.y += getLineHeight(); y1 = cursor.y + getOffset().y; cursor.x = eng.getXAtPosition(y1, y1 + getLineHeight()); it = last_it; width = eng.getWidthAtPosition(y1, y1 + getLineHeight(), containing.content_.width); continue; } lines_.emplace_back(line, cursor); lines_.back().width_ = calculateWidth(lines_.back()); // XXX This height needs to be modified later if we have inline elements with a different lineheight lines_.back().height_ = getLineHeight(); cursor.x += lines_.back().width_; if(line->is_end_line) { // update the cursor for the next line cursor.y += getLineHeight(); y1 = cursor.y + getOffset().y; cursor.x = eng.getXAtPosition(y1, y1 + getLineHeight()); } } } int max_w = 0; for(auto& line : lines_) { max_w = std::max(max_w, line.width_); } setContentWidth(max_w); if(!lines_.empty()) { setContentHeight(lines_.back().offset_.y + getLineHeight()); } return cursor; }
void InlineBlockBox::handleLayout(LayoutEngine& eng, const Dimensions& containing) { eng.setCursor(cursor_); layoutChildren(eng); layoutHeight(containing); if(isReplaceable()) { NodePtr node = getNode(); node->setDimensions(rect(0, 0, getWidth() / LayoutEngine::getFixedPointScale(), getHeight() / LayoutEngine::getFixedPointScale())); } // try and fit the box at cursor, failing that we move the cursor and try again. FixedPoint width_at_cursor = eng.getWidthAtPosition(eng.getCursor().y, eng.getCursor().y + getHeight() + getMBPHeight(), containing.content_.width) - eng.getCursor().x + eng.getXAtPosition(eng.getCursor().y, eng.getCursor().y + getHeight() + getMBPHeight()); if(getWidth() + getMBPWidth() > width_at_cursor) { point p = eng.getCursor(); p.y += getLineHeight(); while(eng.hasFloatsAtPosition(p.y, p.y + getHeight() + getMBPHeight()) && getWidth() + getMBPWidth() > width_at_cursor) { width_at_cursor = eng.getWidthAtPosition(p.y, p.y + getHeight() + getMBPHeight(), containing.content_.width); } p.x = eng.getXAtPosition(p.y, p.y + getHeight() + getMBPHeight()); setContentX(p.x); setContentY(p.y); p.y += getHeight() + getMBPHeight(); p.x = eng.getXAtPosition(p.y, p.y + getLineHeight()); eng.setCursor(p); } else { setContentX(eng.getCursor().x); // XXX if height is greater than other objects on line we need to increase lineheight. setContentY(eng.getCursor().y); eng.setCursor(point(getLeft() + getWidth() + getMBPRight(), eng.getCursor().y)); } }
void BlockBox::handleLayout(LayoutEngine& eng, const Dimensions& containing) { if(isReplaceable()) { layoutChildren(eng); } else { layoutChildren(eng); layoutHeight(containing); } if(isFloat()) { FixedPoint top = 0; const FixedPoint lh = getStyleNode()->getHeight()->isAuto() ? getLineHeight() : getStyleNode()->getHeight()->getLength().compute(containing.content_.height); const FixedPoint box_w = getDimensions().content_.width; FixedPoint y = 0; FixedPoint x = 0; FixedPoint y1 = y + getOffset().y; FixedPoint left = getStyleNode()->getFloat() == Float::LEFT ? eng.getXAtPosition(y1, y1 + lh) + x : eng.getX2AtPosition(y1, y1 + lh); FixedPoint w = eng.getWidthAtPosition(y1, y1 + lh, containing.content_.width); bool placed = false; while(!placed) { if(w >= box_w) { left = left - (getStyleNode()->getFloat() == Float::LEFT ? x : box_w) + getMBPLeft(); top = y + getMBPTop() + containing.content_.height; placed = true; } else { y += lh; y1 = y + getOffset().y; left = getStyleNode()->getFloat() == Float::LEFT ? eng.getXAtPosition(y1, y1 + lh) + x : eng.getX2AtPosition(y1, y1 + lh); w = eng.getWidthAtPosition(y1, y1 + lh, containing.content_.width); } } setContentX(left); setContentY(top); } else { for(auto& child : getChildren()) { setContentHeight(child->getTop() + child->getHeight() + child->getMBPBottom()); } } }
void Box::layout(LayoutEngine& eng, const Dimensions& ocontaining) { auto containing = ocontaining; auto styles = getStyleNode(); std::unique_ptr<LayoutEngine::FloatContextManager> fcm; if(getParent() && getParent()->isFloat()) { fcm.reset(new LayoutEngine::FloatContextManager(eng, FloatList())); } point cursor; // If we have a clear flag set, then move the cursor in the layout engine to clear appropriate floats. if(node_ != nullptr) { eng.moveCursorToClearFloats(node_->getClear(), cursor); } NodePtr node = getNode(); std::unique_ptr<RenderContext::Manager> ctx_manager; if(node != nullptr) { ctx_manager.reset(new RenderContext::Manager(node->getProperties())); } if(styles != nullptr) { auto ovf = styles->getOverflow(); // this is kind of a hack, since we really want to avoid re-running layout //if(ovf == Overflow::SCROLL || ovf == Overflow::AUTO) { containing.content_.width -= scrollbar_default_width * LayoutEngine::getFixedPointScale(); //} } handlePreChildLayout(eng, containing); if(node_ != nullptr) { const std::vector<StyleNodePtr>& node_children = node_->getChildren(); if(!node_children.empty()) { boxes_ = eng.layoutChildren(node_children, shared_from_this()); } } for(auto& child : boxes_) { if(child->isFloat()) { handlePreChildLayout3(eng, containing); child->layout(eng, dimensions_); handlePostFloatChildLayout(eng, child); eng.addFloat(child); } } offset_ = (getParent() != nullptr ? getParent()->getOffset() : point()) + point(dimensions_.content_.x, dimensions_.content_.y); if(isBlockBox()) { const FixedPoint y1 = offset_.y; point p(eng.getXAtPosition(y1, y1 + getLineHeight()), 0); eng.setCursor(p); } handlePreChildLayout2(eng, containing); for(auto& child : boxes_) { if(!child->isFloat()) { handlePreChildLayout3(eng, containing); child->layout(eng, dimensions_); handlePostChildLayout(eng, child); } } handleLayout(eng, containing); //layoutAbsolute(eng, containing); for(auto& child : boxes_) { child->postParentLayout(eng, dimensions_); } // need to call this after doing layout, since we need to now what the computed padding/border values are. border_info_.init(dimensions_); background_info_.init(dimensions_); if(isBlockBox() && !isFloat()) { point p; p.y = getTop() + getHeight() + getMBPBottom(); p.x = eng.getXAtPosition(p.y, p.y + getLineHeight()); eng.setCursor(p); } precss_content_height_ = dimensions_.content_.height; if(isBlockBox() && styles != nullptr) { auto css_h = styles->getHeight(); if(!css_h->isAuto()) { setContentHeight(css_h->getLength().compute(containing.content_.height)); } } eng.closeLineBox(); }
void ListItemBox::handleLayout(LayoutEngine& eng, const Dimensions& containing) { auto lst = getStyleNode()->getListStyleType(); switch(lst) { case ListStyleType::DISC: /* is the default */ break; case ListStyleType::CIRCLE: marker_ = utils::codepoint_to_utf8(marker_circle); break; case ListStyleType::SQUARE: marker_ = utils::codepoint_to_utf8(marker_square); break; case ListStyleType::DECIMAL: { std::ostringstream ss; ss << std::dec << count_ << "."; marker_ = ss.str(); break; } case ListStyleType::DECIMAL_LEADING_ZERO: { std::ostringstream ss; ss << std::dec << std::setfill('0') << std::setw(2) << count_ << "."; marker_ = ss.str(); break; } case ListStyleType::LOWER_ROMAN: if(count_ < 4000) { marker_ = to_roman(count_, true) + "."; } break; case ListStyleType::UPPER_ROMAN: if(count_ < 4000) { marker_ = to_roman(count_, false) + "."; } break; case ListStyleType::LOWER_GREEK: if(count_ <= (marker_lower_greek_end - marker_lower_greek + 1)) { marker_ = utils::codepoint_to_utf8(marker_lower_greek + count_) + "."; } break; case ListStyleType::LOWER_ALPHA: case ListStyleType::LOWER_LATIN: if(count_ <= (marker_lower_latin_end - marker_lower_latin + 1)) { marker_ = utils::codepoint_to_utf8(marker_lower_latin + count_) + "."; } break; case ListStyleType::UPPER_ALPHA: case ListStyleType::UPPER_LATIN: if(count_ <= (marker_upper_latin_end - marker_upper_latin + 1)) { marker_ = utils::codepoint_to_utf8(marker_upper_latin + count_) + "."; } break; case ListStyleType::ARMENIAN: if(count_ <= (marker_armenian_end - marker_armenian + 1)) { marker_ = utils::codepoint_to_utf8(marker_armenian + count_) + "."; } break; case ListStyleType::GEORGIAN: if(count_ <= (marker_georgian_end - marker_georgian + 1)) { marker_ = utils::codepoint_to_utf8(marker_georgian + count_) + "."; } break; case ListStyleType::NONE: default: marker_.clear(); break; } FixedPoint top = getMBPTop() + containing.content_.height; FixedPoint left = getMBPLeft(); if(isFloat()) { // XXX fixme to use a more intelligent approach than iterating every pixel! const FixedPoint lh = 65536;//getDimensions().content_.height; const FixedPoint box_w = getDimensions().content_.width; FixedPoint y = getMBPTop(); FixedPoint x = getMBPLeft(); FixedPoint y1 = y + getOffset().y; left = getStyleNode()->getFloat() == Float::LEFT ? eng.getXAtPosition(y1, y1 + lh) + x : eng.getX2AtPosition(y1, y1 + lh); FixedPoint w = eng.getWidthAtPosition(y1, y1 + lh, containing.content_.width); bool placed = false; while(!placed) { if(w >= box_w) { left = left - (getStyleNode()->getFloat() == Float::LEFT ? x : box_w); top = y; placed = true; } else { y += lh; y1 = y + getOffset().y; left = getStyleNode()->getFloat() == Float::LEFT ? eng.getXAtPosition(y1, y1 + lh) + x : eng.getX2AtPosition(y1, y1 + lh); w = eng.getWidthAtPosition(y1, y1 + lh, containing.content_.width); } } } setContentX(left); setContentY(top); auto& css_height = getStyleNode()->getHeight(); if(!css_height->isAuto()) { FixedPoint h = css_height->getLength().compute(containing.content_.height); //if(h > child_height_) { // /* apply overflow properties */ //} setContentHeight(h); } }