Example #1
0
	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));
		}
	}
Example #3
0
	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());
			}
		}
	}
Example #4
0
	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);
		}

	}