📄 bidi.cpp
字号:
BidiContext *startEmbed;
if( style()->direction() == LTR ) {
startEmbed = new BidiContext( 0, QChar::DirL );
bidi.status.eor = QChar::DirL;
} else {
startEmbed = new BidiContext( 1, QChar::DirR );
bidi.status.eor = QChar::DirR;
}
startEmbed->ref();
bidi.status.lastStrong = QChar::DirON;
bidi.status.last = QChar::DirON;
bidi.context = startEmbed;
if (!smidpoints)
smidpoints = new QMemArray<BidiIterator>;
sNumMidpoints = 0;
sCurrMidpoint = 0;
sCompactFirstBidiRun = sCompactLastBidiRun = 0;
sCompactBidiRunCount = 0;
// We want to skip ahead to the first dirty line
BidiIterator start;
RootInlineBox* startLine = determineStartPosition(fullLayout, start, bidi);
// We also find the first clean line and extract these lines. We will add them back
// if we determine that we're able to synchronize after handling all our dirty lines.
BidiIterator cleanLineStart;
int endLineYPos;
RootInlineBox* endLine = (fullLayout || !startLine) ?
0 : determineEndPosition(startLine, cleanLineStart, endLineYPos);
if (startLine) {
useRepaintRect = true;
startLineBottom = startLine->bottomOverflow();
repaintRect.setY(kMin(m_height, startLine->topOverflow()));
RenderArena* arena = renderArena();
RootInlineBox* box = startLine;
while (box) {
RootInlineBox* next = box->nextRootBox();
box->deleteLine(arena);
box = next;
}
startLine = 0;
}
BidiIterator end = start;
bool endLineMatched = false;
while (!end.atEnd()) {
start = end;
if (endLine && (endLineMatched = matchedEndLine(start, cleanLineStart, endLine, endLineYPos)))
break;
betweenMidpoints = false;
isLineEmpty = true;
if (m_firstLine && firstChild() && firstChild()->isCompact() && firstChild()->isRenderBlock()) {
buildCompactRuns(firstChild(), bidi);
start.obj = firstChild()->nextSibling();
end = start;
}
end = findNextLineBreak(start, bidi);
if( start.atEnd() ) break;
if (!isLineEmpty) {
bidiReorderLine(start, end, bidi);
// Now that the runs have been ordered, we create the line boxes.
// At the same time we figure out where border/padding/margin should be applied for
// inline flow boxes.
if (sCompactFirstBidiRun) {
// We have a compact line sharing this line. Link the compact runs
// to our runs to create a single line of runs.
sCompactLastBidiRun->nextRun = sFirstBidiRun;
sFirstBidiRun = sCompactFirstBidiRun;
sBidiRunCount += sCompactBidiRunCount;
}
RootInlineBox* lineBox = 0;
if (sBidiRunCount) {
lineBox = constructLine(start, end);
if (lineBox) {
lineBox->setEndsWithBreak(previousLineBrokeCleanly);
// Now we position all of our text runs horizontally.
computeHorizontalPositionsForLine(lineBox, bidi);
// Now position our text runs vertically.
computeVerticalPositionsForLine(lineBox);
deleteBidiRuns(renderArena());
}
}
if (end == start || (!previousLineBrokeCleanly && end.obj && end.obj->style()->whiteSpace() == PRE && end.current() == QChar('\n'))) {
adjustEmbedding = true;
end.increment(bidi);
adjustEmbedding = false;
}
if (lineBox)
lineBox->setLineBreakInfo(end.obj, end.pos);
m_firstLine = false;
newLine();
}
sNumMidpoints = 0;
sCurrMidpoint = 0;
sCompactFirstBidiRun = sCompactLastBidiRun = 0;
sCompactBidiRunCount = 0;
}
startEmbed->deref();
//embed->deref();
if (endLine) {
if (endLineMatched) {
// Note our current y-position for correct repainting when no lines move. If no lines move, we still have to
// repaint up to the maximum of the bottom overflow of the old start line or the bottom overflow of the new last line.
int currYPos = kMax(startLineBottom, m_height);
if (lastRootBox())
currYPos = kMax(currYPos, lastRootBox()->bottomOverflow());
// Attach all the remaining lines, and then adjust their y-positions as needed.
for (RootInlineBox* line = endLine; line; line = line->nextRootBox())
line->attachLine();
// Now apply the offset to each line if needed.
int delta = m_height - endLineYPos;
if (delta)
for (RootInlineBox* line = endLine; line; line = line->nextRootBox())
line->adjustPosition(0, delta);
m_height = lastRootBox()->blockHeight();
m_overflowHeight = kMax(m_height, m_overflowHeight);
int bottomOfLine = lastRootBox()->bottomOverflow();
if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight)
m_overflowHeight = bottomOfLine;
if (delta)
repaintRect.setHeight(kMax(m_overflowHeight-delta, m_overflowHeight) - repaintRect.y());
else
repaintRect.setHeight(currYPos - repaintRect.y());
}
else {
// Delete all the remaining lines.
m_overflowHeight = kMax(m_height, m_overflowHeight);
InlineRunBox* line = endLine;
RenderArena* arena = renderArena();
while (line) {
InlineRunBox* next = line->nextLineBox();
if (!next)
repaintRect.setHeight(kMax(m_overflowHeight, line->bottomOverflow()) - repaintRect.y());
line->deleteLine(arena);
line = next;
}
}
}
}
sNumMidpoints = 0;
sCurrMidpoint = 0;
// in case we have a float on the last line, it might not be positioned up to now.
// This has to be done before adding in the bottom border/padding, or the float will
// include the padding incorrectly. -dwh
positionNewFloats();
// Now add in the bottom border/padding.
m_height += toAdd;
// Always make sure this is at least our height.
m_overflowHeight = kMax(m_height, m_overflowHeight);
// See if any lines spill out of the block. If so, we need to update our overflow width.
checkLinesForOverflow();
if (useRepaintRect) {
repaintRect.setWidth(kMax((int)m_width, m_overflowWidth));
if (repaintRect.height() == 0)
repaintRect.setHeight(kMax(oldLineBottom, m_overflowHeight) - repaintRect.y());
}
if (!firstLineBox() && element() && element()->isContentEditable() && element()->rootEditableElement() == element())
m_height += lineHeight(true);
// See if we have any lines that spill out of our block. If we do, then we will possibly need to
// truncate text.
if (hasTextOverflow)
checkLinesForTextOverflow();
return repaintRect;
#if BIDI_DEBUG > 1
kdDebug(6041) << " ------- bidi end " << this << " -------" << endl;
#endif
//kdDebug() << "RenderBlock::layoutInlineChildren time used " << qt.elapsed() << endl;
//kdDebug(6040) << "height = " << m_height <<endl;
}
RootInlineBox* RenderBlock::determineStartPosition(bool fullLayout, BidiIterator& start, BidiState& bidi)
{
RootInlineBox* curr = 0;
RootInlineBox* last = 0;
RenderObject* startObj = 0;
int pos = 0;
if (fullLayout) {
// Nuke all our lines.
if (firstRootBox()) {
RenderArena* arena = renderArena();
curr = firstRootBox();
while (curr) {
RootInlineBox* next = curr->nextRootBox();
curr->deleteLine(arena);
curr = next;
}
KHTMLAssert(!m_firstLineBox && !m_lastLineBox);
}
}
else {
for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox());
if (curr) {
// We have a dirty line.
if (curr->prevRootBox()) {
// We have a previous line.
if (!curr->prevRootBox()->endsWithBreak())
curr = curr->prevRootBox(); // The previous line didn't break cleanly, so treat it as dirty also.
}
}
else {
// No dirty lines were found.
// If the last line didn't break cleanly, treat it as dirty.
if (lastRootBox() && !lastRootBox()->endsWithBreak())
curr = lastRootBox();
}
// If we have no dirty lines, then last is just the last root box.
last = curr ? curr->prevRootBox() : lastRootBox();
}
m_firstLine = !last;
previousLineBrokeCleanly = !last || last->endsWithBreak();
if (last) {
m_height = last->blockHeight();
int bottomOfLine = last->bottomOverflow();
if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight)
m_overflowHeight = bottomOfLine;
startObj = last->lineBreakObj();
pos = last->lineBreakPos();
}
else
startObj = first(this, bidi, 0);
adjustEmbedding = true;
start = BidiIterator(this, startObj, pos);
adjustEmbedding = false;
return curr;
}
RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, BidiIterator& cleanLineStart,
int& yPos)
{
RootInlineBox* last = 0;
if (!startLine)
last = 0;
else {
for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
if (curr->isDirty())
last = 0;
else if (!last)
last = curr;
}
}
if (!last)
return 0;
cleanLineStart = BidiIterator(this, last->prevRootBox()->lineBreakObj(), last->prevRootBox()->lineBreakPos());
yPos = last->prevRootBox()->blockHeight();
for (RootInlineBox* line = last; line; line = line->nextRootBox())
line->extractLine(); // Disconnect all line boxes from their render objects while preserving
// their connections to one another.
return last;
}
bool RenderBlock::matchedEndLine(const BidiIterator& start, const BidiIterator& endLineStart,
RootInlineBox*& endLine, int& endYPos)
{
if (start == endLineStart) {
// Need to return false in case the first "clean" line is indirectly dirty from
// line wrap changes (i.e. could need to shift this line left or right in
// response to changes on the previous line).
return false;
} else {
// The first clean line doesn't match, but we can check a handful of following lines to try
// to match back up.
static int numLines = 8; // The # of lines we're willing to match against.
RootInlineBox* line = endLine;
for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
if (line->lineBreakObj() == start.obj && line->lineBreakPos() == start.pos) {
// We have a match.
RootInlineBox* result = line->nextRootBox();
// Set our yPos to be the block height of endLine.
if (result)
endYPos = line->blockHeight();
// Now delete the lines that we failed to sync.
RootInlineBox* boxToDelete = endLine;
RenderArena* arena = renderArena();
while (boxToDelete && boxToDelete != result) {
RootInlineBox* next = boxToDelete->nextRootBox();
boxToDelete->deleteLine(arena);
boxToDelete = next;
}
endLine = result;
return result;
}
}
}
return false;
}
static const ushort nonBreakingSpace = 0xa0;
inline bool RenderBlock::skipNonBreakingSpace(BidiIterator &it)
{
if (it.obj->style()->nbspMode() != SPACE || it.current().unicode() != nonBreakingSpace)
return false;
// Do not skip a non-breaking space if it is the first character
// on the first line of a block.
if (m_firstLine && isLineEmpty)
return false;
// Do not skip a non-breaking space if it is the first character
// on a line after a clean line break.
if (!m_firstLine && isLineEmpty && previousLineBrokeCleanly)
return false;
return true;
}
int RenderBlock::skipWhitespace(BidiIterator &it, BidiState &bidi)
{
// FIXME: The entire concept of the skipWhitespace function is flawed, since we really need to be building
// line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned
// elements quite right. In other words, we need to build this function's work into the normal line
// object iteration process.
int w = lineWidth(m_height);
while (!it.atEnd() && (it.obj->isInlineFlow() || (it.obj->style()->whiteSpace() != PRE && !it.obj->isBR() &&
(it.current() == ' ' || it.current() == '\n' ||
skipNonBreakingSpace(it) || it.obj->isFloatingOrPositioned())))) {
if (it.obj->isFlo
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -