📄 rendertext.cpp
字号:
void RenderText::trimmedPrefWidths(int leadWidth, int& beginMinW, bool& beginWS, int& endMinW, bool& endWS, bool& hasBreakableChar, bool& hasBreak, int& beginMaxW, int& endMaxW, int& minW, int& maxW, bool& stripFrontSpaces){ bool collapseWhiteSpace = style()->collapseWhiteSpace(); if (!collapseWhiteSpace) stripFrontSpaces = false; if (m_hasTab || prefWidthsDirty()) calcPrefWidths(leadWidth); beginWS = !stripFrontSpaces && m_hasBeginWS; endWS = m_hasEndWS; int len = textLength(); if (!len || (stripFrontSpaces && m_text->containsOnlyWhitespace())) { beginMinW = 0; endMinW = 0; beginMaxW = 0; endMaxW = 0; minW = 0; maxW = 0; hasBreak = false; return; } minW = m_minWidth; maxW = m_maxWidth; beginMinW = m_beginMinWidth; endMinW = m_endMinWidth; hasBreakableChar = m_hasBreakableChar; hasBreak = m_hasBreak; if ((*m_text)[0] == ' ' || ((*m_text)[0] == '\n' && !style()->preserveNewline()) || (*m_text)[0] == '\t') { const Font& f = style()->font(); // FIXME: This ignores first-line. if (stripFrontSpaces) { const UChar space = ' '; int spaceWidth = f.width(TextRun(&space, 1)); maxW -= spaceWidth; } else maxW += f.wordSpacing(); } stripFrontSpaces = collapseWhiteSpace && m_hasEndWS; if (!style()->autoWrap() || minW > maxW) minW = maxW; // Compute our max widths by scanning the string for newlines. if (hasBreak) { const Font& f = style()->font(); // FIXME: This ignores first-line. bool firstLine = true; beginMaxW = maxW; endMaxW = maxW; for (int i = 0; i < len; i++) { int linelen = 0; while (i + linelen < len && (*m_text)[i + linelen] != '\n') linelen++; if (linelen) { endMaxW = widthFromCache(f, i, linelen, leadWidth + endMaxW); if (firstLine) { firstLine = false; leadWidth = 0; beginMaxW = endMaxW; } i += linelen; } else if (firstLine) { beginMaxW = 0; firstLine = false; leadWidth = 0; } if (i == len - 1) // A <pre> run that ends with a newline, as in, e.g., // <pre>Some text\n\n<span>More text</pre> endMaxW = 0; } }}static inline bool isSpaceAccordingToStyle(UChar c, RenderStyle* style){ return c == ' ' || (c == noBreakSpace && style->nbspMode() == SPACE);}int RenderText::minPrefWidth() const{ if (prefWidthsDirty()) const_cast<RenderText*>(this)->calcPrefWidths(0); return m_minWidth;}int RenderText::maxPrefWidth() const{ if (prefWidthsDirty()) const_cast<RenderText*>(this)->calcPrefWidths(0); return m_maxWidth;}void RenderText::calcPrefWidths(int leadWidth){ ASSERT(m_hasTab || prefWidthsDirty()); m_minWidth = 0; m_beginMinWidth = 0; m_endMinWidth = 0; m_maxWidth = 0; if (isBR()) return; int currMinWidth = 0; int currMaxWidth = 0; m_hasBreakableChar = false; m_hasBreak = false; m_hasTab = false; m_hasBeginWS = false; m_hasEndWS = false; const Font& f = style()->font(); // FIXME: This ignores first-line. int wordSpacing = style()->wordSpacing(); int len = textLength(); const UChar* txt = characters(); bool needsWordSpacing = false; bool ignoringSpaces = false; bool isSpace = false; bool firstWord = true; bool firstLine = true; int nextBreakable = -1; int lastWordBoundary = 0; bool breakNBSP = style()->autoWrap() && style()->nbspMode() == SPACE; bool breakAll = (style()->wordBreak() == BreakAllWordBreak || style()->wordBreak() == BreakWordBreak) && style()->autoWrap(); for (int i = 0; i < len; i++) { UChar c = txt[i]; bool previousCharacterIsSpace = isSpace; bool isNewline = false; if (c == '\n') { if (style()->preserveNewline()) { m_hasBreak = true; isNewline = true; isSpace = false; } else isSpace = true; } else if (c == '\t') { if (!style()->collapseWhiteSpace()) { m_hasTab = true; isSpace = false; } else isSpace = true; } else isSpace = c == ' '; if ((isSpace || isNewline) && !i) m_hasBeginWS = true; if ((isSpace || isNewline) && i == len - 1) m_hasEndWS = true; if (!ignoringSpaces && style()->collapseWhiteSpace() && previousCharacterIsSpace && isSpace) ignoringSpaces = true; if (ignoringSpaces && !isSpace) ignoringSpaces = false; // Ignore spaces and soft hyphens if (ignoringSpaces) { ASSERT(lastWordBoundary == i); lastWordBoundary++; continue; } else if (c == softHyphen) { currMaxWidth += widthFromCache(f, lastWordBoundary, i - lastWordBoundary, leadWidth + currMaxWidth); lastWordBoundary = i + 1; continue; } bool hasBreak = breakAll || isBreakable(txt, i, len, nextBreakable, breakNBSP); bool betweenWords = true; int j = i; while (c != '\n' && !isSpaceAccordingToStyle(c, style()) && c != '\t' && c != softHyphen) { j++; if (j == len) break; c = txt[j]; if (isBreakable(txt, j, len, nextBreakable, breakNBSP)) break; if (breakAll) { betweenWords = false; break; } } int wordLen = j - i; if (wordLen) { int w = widthFromCache(f, i, wordLen, leadWidth + currMaxWidth); currMinWidth += w; if (betweenWords) { if (lastWordBoundary == i) currMaxWidth += w; else currMaxWidth += widthFromCache(f, lastWordBoundary, j - lastWordBoundary, leadWidth + currMaxWidth); lastWordBoundary = j; } bool isSpace = (j < len) && isSpaceAccordingToStyle(c, style()); bool isCollapsibleWhiteSpace = (j < len) && style()->isCollapsibleWhiteSpace(c); if (j < len && style()->autoWrap()) m_hasBreakableChar = true; // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the // last word in the run. if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j)) currMaxWidth += wordSpacing; if (firstWord) { firstWord = false; // If the first character in the run is breakable, then we consider ourselves to have a beginning // minimum width of 0, since a break could occur right before our run starts, preventing us from ever // being appended to a previous text run when considering the total minimum width of the containing block. if (hasBreak) m_hasBreakableChar = true; m_beginMinWidth = hasBreak ? 0 : w; } m_endMinWidth = w; if (currMinWidth > m_minWidth) m_minWidth = currMinWidth; currMinWidth = 0; i += wordLen - 1; } else { // Nowrap can never be broken, so don't bother setting the // breakable character boolean. Pre can only be broken if we encounter a newline. if (style()->autoWrap() || isNewline) m_hasBreakableChar = true; if (currMinWidth > m_minWidth) m_minWidth = currMinWidth; currMinWidth = 0; if (isNewline) { // Only set if preserveNewline was true and we saw a newline. if (firstLine) { firstLine = false; leadWidth = 0; if (!style()->autoWrap()) m_beginMinWidth = currMaxWidth; } if (currMaxWidth > m_maxWidth) m_maxWidth = currMaxWidth; currMaxWidth = 0; } else { currMaxWidth += f.width(TextRun(txt + i, 1, allowTabs(), leadWidth + currMaxWidth)); needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1; } ASSERT(lastWordBoundary == i); lastWordBoundary++; } } if (needsWordSpacing && len > 1 || ignoringSpaces && !firstWord) currMaxWidth += wordSpacing; m_minWidth = max(currMinWidth, m_minWidth); m_maxWidth = max(currMaxWidth, m_maxWidth); if (!style()->autoWrap()) m_minWidth = m_maxWidth; if (style()->whiteSpace() == PRE) { if (firstLine) m_beginMinWidth = m_maxWidth; m_endMinWidth = currMaxWidth; } setPrefWidthsDirty(false);}bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const{ unsigned currPos; for (currPos = from; currPos < from + len && ((*m_text)[currPos] == '\n' || (*m_text)[currPos] == ' ' || (*m_text)[currPos] == '\t'); currPos++) { } return currPos >= (from + len);}int RenderText::firstRunX() const{ return m_firstTextBox ? m_firstTextBox->m_x : 0;}int RenderText::firstRunY() const{ return m_firstTextBox ? m_firstTextBox->m_y : 0;} void RenderText::setSelectionState(SelectionState state){ InlineTextBox* box; RenderObject::setSelectionState(state); if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) { int startPos, endPos; selectionStartEnd(startPos, endPos); if (selectionState() == SelectionStart) { endPos = textLength(); // to handle selection from end of text to end of line if (startPos != 0 && startPos == endPos) startPos = endPos - 1; } else if (selectionState() == SelectionEnd) startPos = 0; for (box = firstTextBox(); box; box = box->nextTextBox()) { if (box->isSelected(startPos, endPos)) { RootInlineBox* line = box->root(); if (line) line->setHasSelectedChildren(true); } } } else { for (box = firstTextBox(); box; box = box->nextTextBox()) { RootInlineBox* line = box->root(); if (line) line->setHasSelectedChildren(state == SelectionInside); } } containingBlock()->setSelectionState(state);}void RenderText::setTextWithOffset(PassRefPtr<StringImpl> text, unsigned offset, unsigned len, bool force){ unsigned oldLen = textLength(); unsigned newLen = text->length(); int delta = newLen - oldLen; unsigned end = len ? offset + len - 1 : offset; RootInlineBox* firstRootBox = 0; RootInlineBox* lastRootBox = 0; bool dirtiedLines = false; // Dirty all text boxes that include characters in between offset and offset+len. for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) { // Text run is entirely before the affected range. if (curr->end() < offset) continue; // Text run is entirely after the affected range. if (curr->start() > end) { curr->offsetRun(delta); RootInlineBox* root = curr->root(); if (!firstRootBox) { firstRootBox = root; if (!dirtiedLines) { // The affected area was in between two runs. Go ahead and mark the root box of // the run after the affected area as dirty. firstRootBox->markDirty(); dirtiedLines = true; } } lastRootBox = root; } else if (curr->end() >= offset && curr->end() <= end) { // Text run overlaps with the left end of the affected range. curr->dirtyLineBoxes(); dirtiedLines = true; } else if (curr->start() <= offset && curr->end() >= end) { // Text run subsumes the affected range. curr->dirtyLineBoxes(); dirtiedLines = true; } else if (curr->start() <= end && curr->end() >= end) { // Text run overlaps with right end of the affected range. curr->dirtyLineBoxes(); dirtiedLines = true; } } // Now we have to walk all of the clean lines and adjust their cached line break information // to reflect our updated offsets. if (lastRootBox) lastRootBox = lastRootBox->nextRootBox(); if (firstRootBox) { RootInlineBox* prev = firstRootBox->prevRootBox(); if (prev) firstRootBox = prev; } for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) { if (curr->lineBreakObj() == this && curr->lineBreakPos() > end) curr->setLineBreakPos(curr->lineBreakPos() + delta); } // If the text node is empty, dirty the line where new text will be inserted. if (!firstTextBox() && parent()) { parent()->dirtyLinesFromChangedChild(this); dirtiedLines = true; } m_linesDirty = dirtiedLines; setText(text, force);}static inline bool isInlineFlowOrEmptyText(RenderObject* o){ if (o->isRenderInline()) return true; if (!o->isText()) return false; StringImpl* text = toRenderText(o)->text(); if (!text) return true; return !text->length();}UChar RenderText::previousCharacter(){ // find previous text renderer if one exists RenderObject* previousText = this; while ((previousText = previousText->previousInPreOrder())) if (!isInlineFlowOrEmptyText(previousText)) break; UChar prev = ' '; if (previousText && previousText->isText()) if (StringImpl* previousString = toRenderText(previousText)->text()) prev = (*previousString)[previousString->length() - 1];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -