RenderedStringTextComponent* RenderedColourStringTextComponent::split(float split_point, bool first_component) { const Font* fnt = d_font ? d_font : System::getSingleton().getDefaultGUIContext().getDefaultFont(); // This is checked, but should never fail, since if we had no font our // extent would be 0 and we would never cause a split to be needed here. if (!fnt) CEGUI_THROW(InvalidRequestException( "RenderedStringTextComponent::split: " "unable to split with no font set.")); // create 'left' side of split and clone our basic configuration RenderedColourStringTextComponent* lhs = new RenderedColourStringTextComponent; lhs->d_padding = d_padding; lhs->d_verticalFormatting = d_verticalFormatting; lhs->d_font = d_font; lhs->d_colours = d_colours; // calculate the 'best' place to split the text size_t left_len = 0; float left_extent = 0.0f; while (left_len < d_text.length()) { size_t token_len = getNextTokenLength(d_text, left_len); // exit loop if no more valid tokens. if (token_len == 0) break; const float token_extent = fnt->getTextExtent(d_text.substr(left_len, token_len)); // does the next token extend past the split point? if (left_extent + token_extent > split_point) { // if it was the first token, split the token itself if (first_component && left_len == 0) left_len = ceguimax(static_cast<size_t>(1), fnt->getCharAtPixel(d_text.substr(0, token_len), split_point)); // left_len is now the character index at which to split the line break; } // add this token to the left side left_len += token_len; left_extent += token_extent; } // perform the split. lhs->d_text = d_text.substr(0, left_len); // here we're trimming leading delimiters from the substring range size_t rhs_start = d_text.find_first_not_of(TextUtils::DefaultWrapDelimiters, left_len); if (rhs_start == String::npos) rhs_start = left_len; d_text = d_text.substr(rhs_start); return lhs; }
/************************************************************************* Format the text into lines as needed by the current formatting options. *************************************************************************/ void MultiLineEditbox::formatText(void) { // clear old formatting data d_lines.clear(); d_widestExtent = 0.0f; String paraText; const Font* fnt = getFont(); if (fnt != NULL) { float areaWidth = getTextRenderArea().getWidth(); String::size_type currPos = 0; String::size_type paraLen; LineInfo line; while (currPos < d_text.length()) { if ((paraLen = d_text.find_first_of(d_lineBreakChars, currPos)) == String::npos) { paraLen = d_text.length() - currPos; } else { ++paraLen -= currPos; } paraText = d_text.substr(currPos, paraLen); if (!d_wordWrap || (areaWidth <= 0.0f)) { // no word wrapping, so we are just one long line. line.d_startIdx = currPos; line.d_length = paraLen; line.d_extent = fnt->getTextExtent(paraText); d_lines.push_back(line); // update widest, if needed. if (line.d_extent > d_widestExtent) { d_widestExtent = line.d_extent; } } // must word-wrap the paragraph text else { String::size_type lineIndex = 0; // while there is text in the string while (lineIndex < paraLen) { String::size_type lineLen = 0; float lineExtent = 0.0f; // loop while we have not reached the end of the paragraph string while (lineLen < (paraLen - lineIndex)) { // get cp / char count of next token size_t nextTokenSize = getNextTokenLength(paraText, lineIndex + lineLen); // get pixel width of the token float tokenExtent = fnt->getTextExtent(paraText.substr(lineIndex + lineLen, nextTokenSize)); // would adding this token would overflow the available width if ((lineExtent + tokenExtent) > areaWidth) { // Was this the first token? if (lineLen == 0) { // get point at which to break the token lineLen = fnt->getCharAtPixel(paraText.substr(lineIndex, nextTokenSize), areaWidth); } // text wraps, exit loop early with line info up until wrap point break; } // add this token to current line lineLen += nextTokenSize; lineExtent += tokenExtent; } // set up line info and add to collection line.d_startIdx = currPos + lineIndex; line.d_length = lineLen; line.d_extent = lineExtent; d_lines.push_back(line); // update widest, if needed. if (lineExtent > d_widestExtent) { d_widestExtent = lineExtent; } // update position in string lineIndex += lineLen; } } // skip to next 'paragraph' in text currPos += paraLen; } } configureScrollbars(); requestRedraw(); }
void MarkupBase::parseBBCodes() { clear(); const float maxwidth = m_viewport.getWidth(); float height = 0.f; float xpos = 0.f; size_t wordcount = 0; PTextLine currentLine; PText textChunk; PImg imgChunk; PTooltipArea tooltipChunk; PLinkArea urlChunk; float linespacing = m_textSpacing; const ActiveText::TextViewVector& v = m_parser.getFormattedOutput(); ActiveText::TextViewVector::const_iterator i = v.begin(); ActiveText::TextViewVector::const_iterator end = v.end(); while(i != end) { const ActiveText::TextView* tv = (*i).get(); size_t pos = tv->start; size_t len = tv->len; size_t last = pos + len; std::string text = m_text.substr(pos, len); if(tv->isNewLine) { height += addTextLine(currentLine); // new paragraph currentLine.reset(new TextLine()); currentLine->area = m_viewport; currentLine->area.m_top = height; currentLine->area.setHeight(0.f); linespacing = tv->isList ? m_listSpacing : m_textSpacing; wordcount = 0; xpos = 0.f; xpos += m_font->getTextExtent(" "); } { textChunk.reset(new Text()); textChunk->start = pos; textChunk->col = tv->col; textChunk->font = tv->font; textChunk->selcol = tv->col; textChunk->selcol.invertColour(); textChunk->area.m_left = xpos; textChunk->area.setHeight(textChunk->font->getLineSpacing()); textChunk->area.setWidth(0.f); } if(tv->isTooltip) { if(tooltipChunk) { if(tooltipChunk->parent != tv->tooltipnode) { m_tooltips.push_back(tooltipChunk); tooltipChunk.reset(new TooltipArea()); tooltipChunk->parent = tv->tooltipnode; tooltipChunk->tooltip = tv->tooltip; } } else { tooltipChunk.reset(new TooltipArea()); tooltipChunk->parent = tv->tooltipnode; tooltipChunk->tooltip = tv->tooltip; } } if(tv->isURL) { if(urlChunk) { if(urlChunk->parent != tv->urlnode) { m_links.push_back(urlChunk); urlChunk.reset(new LinkArea()); urlChunk->parent = tv->urlnode; urlChunk->type = tv->type; urlChunk->id = tv->id; } } else { urlChunk.reset(new LinkArea()); urlChunk->parent = tv->urlnode; urlChunk->type = tv->type; urlChunk->id = tv->id; } } if(!tv->isImage) { // text while(pos < last) { bool white = false; size_t nextTokenLen = getNextTokenLength(m_text, pos, white); if(pos + nextTokenLen > last) nextTokenLen = last - pos; float xlen = tv->font->getTextExtent(m_text.substr(pos, nextTokenLen)); // if text isn't fit anyway (and must be alone on a string) if(xlen > maxwidth && wordcount == 0) xlen = maxwidth - xpos; if(xpos + xlen > maxwidth) { textChunk->len = pos - textChunk->start; if(white) { // don't allow whitespace in new line pos += nextTokenLen; } currentLine->addChunk(textChunk, linespacing); if(tv->isTooltip) tooltipChunk->masked.push_back(textChunk); if(tv->isURL) urlChunk->masked.push_back(textChunk); height += addTextLine(currentLine); { // new line currentLine.reset(new TextLine()); currentLine->area = m_viewport; currentLine->area.m_top = height; currentLine->area.setHeight(0.f); linespacing = tv->isList ? m_listSpacing : m_textSpacing; wordcount = 0; xpos = 0.f; } { textChunk.reset(new Text()); textChunk->start = pos; textChunk->col = tv->col; textChunk->font = tv->font; textChunk->selcol = tv->col; textChunk->selcol.invertColour(); textChunk->area.m_left = xpos; textChunk->area.setHeight(textChunk->font->getLineSpacing()); textChunk->area.setWidth(0.f); } } else { // add next word until width or tag pos += nextTokenLen; xpos += xlen; wordcount++; textChunk->area.m_right = xpos; } } // end chunk textChunk->len = pos - textChunk->start; currentLine->addChunk(textChunk, linespacing); if(tv->isTooltip) tooltipChunk->masked.push_back(textChunk); if(tv->isURL) urlChunk->masked.push_back(textChunk); } else { // image imgChunk.reset(new Img()); const Image* img = tv->img; imgChunk->img = img; if(img) { Size imgsize = img->GetSize(); if(imgsize.width > maxwidth - xpos) { height += addTextLine(currentLine); { // new line currentLine.reset(new TextLine()); currentLine->area = m_viewport; currentLine->area.m_top = height; currentLine->area.setHeight(0.f); linespacing = tv->isList ? m_listSpacing : m_textSpacing; wordcount = 0; xpos = 0.f; } } imgChunk->area.m_left = m_viewport.m_left + xpos; imgChunk->area.m_top = /*m_viewport.m_top*/ + currentLine->area.m_top; imgChunk->area.m_bottom = imgChunk->area.m_top + imgsize.height; imgChunk->area.m_right = imgChunk->area.m_left + imgsize.width; xpos += imgsize.width; wordcount++; currentLine->addChunk(imgChunk); m_images.push_back(imgChunk); if(tv->isTooltip) tooltipChunk->maskedimg.push_back(imgChunk); if(tv->isURL) urlChunk->maskedimg.push_back(imgChunk); } } ++i; } if(currentLine && !currentLine->isEmpty()) height += addTextLine(currentLine); if(tooltipChunk && !tooltipChunk->isEmpty()) m_tooltips.push_back(tooltipChunk); if(urlChunk && !urlChunk->isEmpty()) m_links.push_back(urlChunk); m_viewport.m_bottom = m_viewport.m_top + height; m_area.m_bottom = m_viewport.m_bottom + m_margin.m_bottom; }