📄 bidi.cpp
字号:
// clip.
// FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely
// difficult to figure out (especially in the middle of doing layout), and is really an esoteric pile of nonsense
// anyway, so we won't worry about following the draft here.
bool hasTextOverflow = style()->textOverflow() && hasOverflowClip();
// Walk all the lines and delete our ellipsis line boxes if they exist.
if (hasTextOverflow)
deleteEllipsisLineBoxes();
int oldLineBottom = lastRootBox() ? lastRootBox()->bottomOverflow() : m_height;
int startLineBottom = 0;
if (firstChild()) {
// layout replaced elements
bool endOfInline = false;
RenderObject *o = first(this, bidi, false);
bool hasFloat = false;
while (o) {
if (o->isReplaced() || o->isFloating() || o->isPositioned()) {
if (relayoutChildren || o->style()->width().isPercent() || o->style()->height().isPercent())
o->setChildNeedsLayout(true, false);
if (o->isPositioned())
o->containingBlock()->insertPositionedObject(o);
else {
if (o->isFloating())
hasFloat = true;
else if (fullLayout || o->needsLayout()) // Replaced elements
o->dirtyLineBoxes(fullLayout);
o->layoutIfNeeded();
}
}
else if (o->isText() || (o->isInlineFlow() && !endOfInline)) {
if (fullLayout || o->selfNeedsLayout())
o->dirtyLineBoxes(fullLayout);
o->setNeedsLayout(false);
}
o = Bidinext( this, o, bidi, false, &endOfInline);
}
if (hasFloat)
fullLayout = true; // FIXME: Will need to find a way to optimize floats some day.
if (fullLayout && !selfNeedsLayout()) {
setNeedsLayout(true, false); // Mark ourselves as needing a full layout. This way we'll repaint like
// we're supposed to.
if (!document()->view()->needsFullRepaint() && m_layer) {
// Because we waited until we were already inside layout to discover
// that the block really needed a full layout, we missed our chance to repaint the layer
// before layout started. Luckily the layer has cached the repaint rect for its original
// position and size, and so we can use that to make a repaint happen now.
RenderCanvas* c = canvas();
if (c && !c->printingMode())
c->repaintViewRectangle(m_layer->repaintRect());
}
}
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 = startEmbed->dir;
bidi.status.last = startEmbed->dir;
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)
return true; // The common case. All the data we already have is correct.
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()) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -