New and improved vertical alignment code

This commit is contained in:
kipp%netscape.com 1998-10-13 20:42:08 +00:00
Родитель 9a915024a8
Коммит 2719cddbf8
2 изменённых файлов: 332 добавлений и 295 удалений

Просмотреть файл

@ -23,7 +23,6 @@
#include "nsISpaceManager.h" #include "nsISpaceManager.h"
#include "nsIStyleContext.h" #include "nsIStyleContext.h"
#include "nsCSSLayout.h"
#include "nsFrameReflowState.h" #include "nsFrameReflowState.h"
#include "nsHTMLContainerFrame.h" #include "nsHTMLContainerFrame.h"
#include "nsHTMLIIDs.h" #include "nsHTMLIIDs.h"
@ -33,11 +32,11 @@
// frames falling outside the parent frame and wrap them in a view // frames falling outside the parent frame and wrap them in a view
// when it happens. // when it happens.
// XXX move the nsCSSLayout alignment code here? Will body frame be
// using it?
// XXX handle DIR=right-to-left // XXX handle DIR=right-to-left
// XXX remove support for block reflow from this and move it into its
// own class (nsBlockReflow?)
nsInlineReflow::nsInlineReflow(nsLineLayout& aLineLayout, nsInlineReflow::nsInlineReflow(nsLineLayout& aLineLayout,
nsFrameReflowState& aOuterReflowState, nsFrameReflowState& aOuterReflowState,
nsHTMLContainerFrame* aOuterFrame, nsHTMLContainerFrame* aOuterFrame,
@ -51,12 +50,15 @@ nsInlineReflow::nsInlineReflow(nsLineLayout& aLineLayout,
mSpaceManager = aLineLayout.mSpaceManager; mSpaceManager = aLineLayout.mSpaceManager;
NS_ASSERTION(nsnull != mSpaceManager, "caller must have space manager"); NS_ASSERTION(nsnull != mSpaceManager, "caller must have space manager");
mOuterFrame = aOuterFrame; mOuterFrame = aOuterFrame;
mFrameData = mFrameDataBuf; mFrameDataBase = mFrameDataBuf;
mNumFrameData = sizeof(mFrameDataBuf) / sizeof(mFrameDataBuf[0]); mNumFrameData = sizeof(mFrameDataBuf) / sizeof(mFrameDataBuf[0]);
} }
nsInlineReflow::~nsInlineReflow() nsInlineReflow::~nsInlineReflow()
{ {
if (mFrameDataBase != mFrameDataBuf) {
delete [] mFrameDataBase;
}
} }
void void
@ -71,7 +73,6 @@ nsInlineReflow::Init(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
mRightEdge = aX + aWidth; mRightEdge = aX + aWidth;
} }
mTopEdge = aY; mTopEdge = aY;
mY = aY;
if (NS_UNCONSTRAINEDSIZE == aHeight) { if (NS_UNCONSTRAINEDSIZE == aHeight) {
mBottomEdge = NS_UNCONSTRAINEDSIZE; mBottomEdge = NS_UNCONSTRAINEDSIZE;
} }
@ -83,7 +84,7 @@ nsInlineReflow::Init(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
mIsBlock = PR_FALSE; mIsBlock = PR_FALSE;
mIsFirstChild = PR_FALSE; mIsFirstChild = PR_FALSE;
mFirstFrame = nsnull; mFrameData = nsnull;
mFrameNum = 0; mFrameNum = 0;
mMaxElementSize.width = 0; mMaxElementSize.width = 0;
mMaxElementSize.height = 0; mMaxElementSize.height = 0;
@ -107,7 +108,6 @@ nsInlineReflow::UpdateBand(nscoord aX, nscoord aY,
mRightEdge = aX + aWidth; mRightEdge = aX + aWidth;
} }
mTopEdge = aY; mTopEdge = aY;
mY = aY;
if (NS_UNCONSTRAINEDSIZE == aHeight) { if (NS_UNCONSTRAINEDSIZE == aHeight) {
mBottomEdge = NS_UNCONSTRAINEDSIZE; mBottomEdge = NS_UNCONSTRAINEDSIZE;
} }
@ -123,7 +123,8 @@ nsInlineReflow::UpdateFrames()
{ {
if (NS_STYLE_DIRECTION_LTR == mOuterReflowState.mDirection) { if (NS_STYLE_DIRECTION_LTR == mOuterReflowState.mDirection) {
if (mPlacedLeftFloater) { if (mPlacedLeftFloater) {
nsIFrame* frame = mFirstFrame; // XXX revise loop
nsIFrame* frame = mFrameDataBase->mFrame;
PRInt32 n = mFrameNum; PRInt32 n = mFrameNum;
while (--n >= 0) { while (--n >= 0) {
nsRect r; nsRect r;
@ -145,8 +146,8 @@ nsInlineReflow::GetDisplay()
if (nsnull != mDisplay) { if (nsnull != mDisplay) {
return mDisplay; return mDisplay;
} }
mFrame->GetStyleData(eStyleStruct_Display, mFrameData->mFrame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&)mDisplay); (const nsStyleStruct*&)mDisplay);
return mDisplay; return mDisplay;
} }
@ -156,8 +157,8 @@ nsInlineReflow::GetPosition()
if (nsnull != mPosition) { if (nsnull != mPosition) {
return mPosition; return mPosition;
} }
mFrame->GetStyleData(eStyleStruct_Position, mFrameData->mFrame->GetStyleData(eStyleStruct_Position,
(const nsStyleStruct*&)mPosition); (const nsStyleStruct*&)mPosition);
return mPosition; return mPosition;
} }
@ -167,11 +168,12 @@ nsInlineReflow::GetSpacing()
if (nsnull != mSpacing) { if (nsnull != mSpacing) {
return mSpacing; return mSpacing;
} }
mFrame->GetStyleData(eStyleStruct_Spacing, mFrameData->mFrame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&)mSpacing); (const nsStyleStruct*&)mSpacing);
return mSpacing; return mSpacing;
} }
// XXX use frameType instead after constructing a reflow state
PRBool PRBool
nsInlineReflow::TreatFrameAsBlockFrame() nsInlineReflow::TreatFrameAsBlockFrame()
{ {
@ -197,23 +199,37 @@ nsInlineReflow::TreatFrameAsBlockFrame()
return PR_FALSE; return PR_FALSE;
} }
void nsresult
nsInlineReflow::SetFrame(nsIFrame* aFrame) nsInlineReflow::SetFrame(nsIFrame* aFrame)
{ {
if (nsnull == mFirstFrame) { // Make sure we have a PerFrameData for this frame
mFirstFrame = aFrame; PRInt32 frameNum = mFrameNum;
if (frameNum == mNumFrameData) {
mNumFrameData *= 2;
PerFrameData* newData = new PerFrameData[mNumFrameData];
if (nsnull == newData) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsCRT::memcpy(newData, mFrameDataBase, sizeof(PerFrameData) * frameNum);
if (mFrameDataBase != mFrameDataBuf) {
delete [] mFrameDataBase;
}
mFrameDataBase = newData;
} }
mFrameData = mFrameDataBase + mFrameNum;
// We can break before the frame if we placed at least one frame on // We can break before the frame if we placed at least one frame on
// the line. // the line.
mCanBreakBeforeFrame = mLineLayout.GetPlacedFrames() > 0; mCanBreakBeforeFrame = mLineLayout.GetPlacedFrames() > 0;
mFrame = aFrame; mFrameData->mFrame = aFrame;
mDisplay = nsnull; mDisplay = nsnull;
mSpacing = nsnull; mSpacing = nsnull;
mPosition = nsnull; mPosition = nsnull;
mTreatFrameAsBlock = TreatFrameAsBlockFrame(); mTreatFrameAsBlock = TreatFrameAsBlockFrame();
mIsInlineAware = PR_FALSE; mIsInlineAware = PR_FALSE;
return NS_OK;
} }
nsReflowStatus nsReflowStatus
@ -226,7 +242,10 @@ nsInlineReflow::ReflowFrame(nsIFrame* aFrame)
: nsnull); : nsnull);
// Prepare for reflowing the frame // Prepare for reflowing the frame
SetFrame(aFrame); nsresult rv = SetFrame(aFrame);
if (NS_OK != rv) {
return rv;
}
// Do a quick check and see if we are trying to place a block on a // Do a quick check and see if we are trying to place a block on a
// line that already has a placed frame on it. // line that already has a placed frame on it.
@ -265,18 +284,20 @@ nsInlineReflow::ReflowFrame(nsIFrame* aFrame)
return result; return result;
} }
// XXX looks more like a method on PerFrameData?
void void
nsInlineReflow::CalculateMargins() nsInlineReflow::CalculateMargins()
{ {
PerFrameData* pfd = mFrameData;
const nsStyleSpacing* spacing = GetSpacing(); const nsStyleSpacing* spacing = GetSpacing();
if (mTreatFrameAsBlock) { if (mTreatFrameAsBlock) {
mMarginFlags = CalculateBlockMarginsFor(mPresContext, mFrame, spacing, pfd->mMarginFlags = CalculateBlockMarginsFor(mPresContext, pfd->mFrame,
mMargin); spacing, pfd->mMargin);
} }
else { else {
// Get the margins from the style system // Get the margins from the style system
spacing->CalcMarginFor(mFrame, mMargin); spacing->CalcMarginFor(pfd->mFrame, pfd->mMargin);
mMarginFlags = 0; pfd->mMarginFlags = 0;
} }
} }
@ -323,7 +344,7 @@ void
nsInlineReflow::ApplyTopLeftMargins() nsInlineReflow::ApplyTopLeftMargins()
{ {
mFrameX = mX; mFrameX = mX;
mFrameY = mY; mFrameY = mTopEdge;
// Compute left margin // Compute left margin
nscoord leftMargin = 0; nscoord leftMargin = 0;
@ -340,7 +361,7 @@ nsInlineReflow::ApplyTopLeftMargins()
break; break;
case NS_STYLE_FLOAT_NONE: case NS_STYLE_FLOAT_NONE:
leftMargin = mMargin.left; leftMargin = mFrameData->mMargin.left;
break; break;
} }
mFrameX += leftMargin; mFrameX += leftMargin;
@ -349,18 +370,20 @@ nsInlineReflow::ApplyTopLeftMargins()
PRBool PRBool
nsInlineReflow::ComputeAvailableSize() nsInlineReflow::ComputeAvailableSize()
{ {
PerFrameData* pfd = mFrameData;
// Compute the available size from the outer's perspective // Compute the available size from the outer's perspective
if (NS_UNCONSTRAINEDSIZE == mRightEdge) { if (NS_UNCONSTRAINEDSIZE == mRightEdge) {
mFrameAvailSize.width = NS_UNCONSTRAINEDSIZE; mFrameAvailSize.width = NS_UNCONSTRAINEDSIZE;
} }
else { else {
mFrameAvailSize.width = mRightEdge - mFrameX - mMargin.right; mFrameAvailSize.width = mRightEdge - mFrameX - pfd->mMargin.right;
} }
if (NS_UNCONSTRAINEDSIZE == mBottomEdge) { if (NS_UNCONSTRAINEDSIZE == mBottomEdge) {
mFrameAvailSize.height = NS_UNCONSTRAINEDSIZE; mFrameAvailSize.height = NS_UNCONSTRAINEDSIZE;
} }
else { else {
mFrameAvailSize.height = mBottomEdge - mFrameY - mMargin.bottom; mFrameAvailSize.height = mBottomEdge - mFrameY - pfd->mMargin.bottom;
} }
// Give up now if there is no chance. Note that we allow a reflow if // Give up now if there is no chance. Note that we allow a reflow if
@ -389,19 +412,20 @@ nsInlineReflow::ReflowFrame(nsHTMLReflowMetrics& aMetrics,
// line). In this case the reason will be wrong so we need to check // line). In this case the reason will be wrong so we need to check
// the frame state. // the frame state.
nsReflowReason reason = eReflowReason_Resize; nsReflowReason reason = eReflowReason_Resize;
nsIFrame* frame = mFrameData->mFrame;
nsFrameState state; nsFrameState state;
mFrame->GetFrameState(state); frame->GetFrameState(state);
if (NS_FRAME_FIRST_REFLOW & state) { if (NS_FRAME_FIRST_REFLOW & state) {
reason = eReflowReason_Initial; reason = eReflowReason_Initial;
} }
else if (mOuterReflowState.mNextRCFrame == mFrame) { else if (mOuterReflowState.mNextRCFrame == frame) {
reason = eReflowReason_Incremental; reason = eReflowReason_Incremental;
// Make sure we only incrementally reflow once // Make sure we only incrementally reflow once
mOuterReflowState.mNextRCFrame = nsnull; mOuterReflowState.mNextRCFrame = nsnull;
} }
// Setup reflow state for reflowing the frame // Setup reflow state for reflowing the frame
nsHTMLReflowState reflowState(mPresContext, mFrame, mOuterReflowState, nsHTMLReflowState reflowState(mPresContext, frame, mOuterReflowState,
mFrameAvailSize); mFrameAvailSize);
if (!mTreatFrameAsBlock) { if (!mTreatFrameAsBlock) {
mIsInlineAware = PR_TRUE; mIsInlineAware = PR_TRUE;
@ -415,7 +439,7 @@ nsInlineReflow::ReflowFrame(nsHTMLReflowMetrics& aMetrics,
nscoord y = mFrameY; nscoord y = mFrameY;
nsIHTMLReflow* htmlReflow; nsIHTMLReflow* htmlReflow;
mFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); frame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow);
htmlReflow->WillReflow(mPresContext); htmlReflow->WillReflow(mPresContext);
aBounds.x = x; aBounds.x = x;
@ -442,8 +466,8 @@ nsInlineReflow::ReflowFrame(nsHTMLReflowMetrics& aMetrics,
// the NS_FRAME_FIRST_REFLOW bit is cleared so that never give it an // the NS_FRAME_FIRST_REFLOW bit is cleared so that never give it an
// initial reflow reason again. // initial reflow reason again.
if (eReflowReason_Initial == reason) { if (eReflowReason_Initial == reason) {
mFrame->GetFrameState(state); frame->GetFrameState(state);
mFrame->SetFrameState(state & ~NS_FRAME_FIRST_REFLOW); frame->SetFrameState(state & ~NS_FRAME_FIRST_REFLOW);
} }
if (!NS_INLINE_IS_BREAK_BEFORE(aStatus)) { if (!NS_INLINE_IS_BREAK_BEFORE(aStatus)) {
@ -453,21 +477,21 @@ nsInlineReflow::ReflowFrame(nsHTMLReflowMetrics& aMetrics,
// a next-in-flow where it ends up). // a next-in-flow where it ends up).
if (NS_FRAME_IS_COMPLETE(aStatus)) { if (NS_FRAME_IS_COMPLETE(aStatus)) {
nsIFrame* kidNextInFlow; nsIFrame* kidNextInFlow;
mFrame->GetNextInFlow(kidNextInFlow); frame->GetNextInFlow(kidNextInFlow);
if (nsnull != kidNextInFlow) { if (nsnull != kidNextInFlow) {
// Remove all of the childs next-in-flows. Make sure that we ask // Remove all of the childs next-in-flows. Make sure that we ask
// the right parent to do the removal (it's possible that the // the right parent to do the removal (it's possible that the
// parent is not this because we are executing pullup code) // parent is not this because we are executing pullup code)
nsHTMLContainerFrame* parent; nsHTMLContainerFrame* parent;
mFrame->GetGeometricParent((nsIFrame*&) parent); frame->GetGeometricParent((nsIFrame*&) parent);
parent->DeleteChildsNextInFlow(mPresContext, mFrame); parent->DeleteChildsNextInFlow(mPresContext, frame);
} }
} }
} }
NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW, NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
("nsInlineReflow::ReflowFrame: frame=%p reflowStatus=%x %saware", ("nsInlineReflow::ReflowFrame: frame=%p reflowStatus=%x %saware",
mFrame, aStatus, mIsInlineAware ? "" :"not ")); frame, aStatus, mIsInlineAware ? "" :"not "));
return !NS_INLINE_IS_BREAK_BEFORE(aStatus); return !NS_INLINE_IS_BREAK_BEFORE(aStatus);
} }
@ -487,6 +511,8 @@ nsInlineReflow::CanPlaceFrame(nsHTMLReflowMetrics& aMetrics,
nsRect& aBounds, nsRect& aBounds,
nsReflowStatus& aStatus) nsReflowStatus& aStatus)
{ {
PerFrameData* pfd = mFrameData;
// Compute right margin to use // Compute right margin to use
mRightMargin = 0; mRightMargin = 0;
if (0 != aBounds.width) { if (0 != aBounds.width) {
@ -503,7 +529,7 @@ nsInlineReflow::CanPlaceFrame(nsHTMLReflowMetrics& aMetrics,
break; break;
case NS_STYLE_FLOAT_NONE: case NS_STYLE_FLOAT_NONE:
mRightMargin = mMargin.right; mRightMargin = pfd->mMargin.right;
break; break;
} }
} }
@ -553,6 +579,8 @@ nsInlineReflow::CanPlaceFrame(nsHTMLReflowMetrics& aMetrics,
void void
nsInlineReflow::PlaceFrame(nsHTMLReflowMetrics& aMetrics, nsRect& aBounds) nsInlineReflow::PlaceFrame(nsHTMLReflowMetrics& aMetrics, nsRect& aBounds)
{ {
PerFrameData* pfd = mFrameData;
// Remember this for later... // Remember this for later...
if (mTreatFrameAsBlock) { if (mTreatFrameAsBlock) {
mIsBlock = PR_TRUE; mIsBlock = PR_TRUE;
@ -565,10 +593,11 @@ nsInlineReflow::PlaceFrame(nsHTMLReflowMetrics& aMetrics, nsRect& aBounds)
aBounds.y = 0; aBounds.y = 0;
emptyFrame = PR_TRUE; emptyFrame = PR_TRUE;
} }
mFrame->SetRect(aBounds); pfd->mBounds = aBounds;
// Record ascent and update max-ascent and max-descent values // Record ascent and update max-ascent and max-descent values
SetFrameData(aMetrics); pfd->mAscent = aMetrics.ascent;
pfd->mDescent = aMetrics.descent;
mFrameNum++; mFrameNum++;
// If the band was updated during the reflow of that frame then we // If the band was updated during the reflow of that frame then we
@ -598,7 +627,7 @@ nsInlineReflow::PlaceFrame(nsHTMLReflowMetrics& aMetrics, nsRect& aBounds)
if (!emptyFrame) { if (!emptyFrame) {
// Inform line layout that we have placed a non-empty frame // Inform line layout that we have placed a non-empty frame
#ifdef NS_DEBUG #ifdef NS_DEBUG
mLineLayout.AddPlacedFrame(mFrame); mLineLayout.AddPlacedFrame(mFrameData->mFrame);
#else #else
mLineLayout.AddPlacedFrame(); mLineLayout.AddPlacedFrame();
#endif #endif
@ -617,266 +646,263 @@ nsInlineReflow::PlaceFrame(nsHTMLReflowMetrics& aMetrics, nsRect& aBounds)
} }
} }
/**
* Store away the ascent value associated with the current frame
*/
nsresult
nsInlineReflow::SetFrameData(const nsHTMLReflowMetrics& aMetrics)
{
PRInt32 frameNum = mFrameNum;
if (frameNum == mNumFrameData) {
mNumFrameData *= 2;
PerFrameData* newData = new PerFrameData[mNumFrameData];
if (nsnull == newData) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsCRT::memcpy(newData, mFrameData, sizeof(PerFrameData) * frameNum);
if (mFrameData != mFrameDataBuf) {
delete [] mFrameData;
}
mFrameData = newData;
}
PerFrameData* pfd = &mFrameData[frameNum];
pfd->mAscent = aMetrics.ascent;
pfd->mDescent = aMetrics.descent;
pfd->mMargin = mMargin;
return NS_OK;
}
// XXX what about ebina's center vs. ncsa-center? // XXX what about ebina's center vs. ncsa-center?
void void
nsInlineReflow::VerticalAlignFrames(nsRect& aLineBox, nsInlineReflow::VerticalAlignFrames(nsRect& aLineBox,
nscoord& aMaxAscent, nscoord& aMaxAscent,
nscoord& aMaxDescent) nscoord& aMaxDescent)
{ {
nscoord x = mLeftEdge; PerFrameData* pfd0 = mFrameDataBase;
nscoord y0 = mTopEdge; PerFrameData* end = pfd0 + mFrameNum;
nscoord width = mX - mLeftEdge;
GetScents(aMaxAscent, aMaxDescent); // Short circuit 99% of this when this code has reflowed a single
nscoord height = aMaxAscent + aMaxDescent; // block frame.
aLineBox.x = mLeftEdge;
if (mFrameNum > 1) { aLineBox.y = mTopEdge;
// Only when we have more than one frame should we do vertical aLineBox.width = mX - mLeftEdge;
// alignment. Sometimes we will have 2 frames with the second one if (mTreatFrameAsBlock) {
// being a block; we don't vertically align then either. This aLineBox.height = pfd0->mBounds.height;
// happens when the first frame is nothing but compressed aMaxAscent = pfd0->mAscent;
// whitespace. aMaxDescent = pfd0->mDescent;
const nsStyleFont* font; pfd0->mFrame->SetRect(aLineBox);
mOuterFrame->GetStyleData(eStyleStruct_Font, return;
(const nsStyleStruct*&)font);
// Determine minimum and maximum y values for the line and
// perform alignment of all children except those requesting bottom
// alignment. The second pass will align bottom children (if any)
nsIFontMetrics* fm = mPresContext.GetMetricsFor(font->mFont);
nsIFrame* kid = mFirstFrame;
nsRect kidRect;
nscoord minY = y0;
nscoord maxY = y0;
PRIntn pass2Kids = 0;
PRIntn kidCount = mFrameNum;
PerFrameData* pfd = mFrameData;
for (; --kidCount >= 0; pfd++) {
nscoord kidAscent = pfd->mAscent;
const nsStyleText* textStyle;
kid->GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&)textStyle);
nsStyleUnit verticalAlignUnit = textStyle->mVerticalAlign.GetUnit();
PRUint8 verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_BASELINE;
kid->GetRect(kidRect);
// Vertically align the child
nscoord kidYTop = 0;
PRBool isPass2Kid = PR_FALSE;
nscoord fontParam;
switch (verticalAlignUnit) {
case eStyleUnit_Coord:
// According to the spec, a positive value "raises" the box by
// the given distance while a negative value "lowers" the box
// by the given distance. Since Y coordinates increase towards
// the bottom of the screen we reverse the sign. All of the
// raising and lowering is done relative to the baseline, so
// we start our adjustments there.
kidYTop = aMaxAscent - kidAscent; // get baseline first
kidYTop -= textStyle->mVerticalAlign.GetCoordValue();
break;
case eStyleUnit_Percent:
pass2Kids++;
isPass2Kid = PR_TRUE;
break;
case eStyleUnit_Enumerated:
verticalAlignEnum = textStyle->mVerticalAlign.GetIntValue();
switch (verticalAlignEnum) {
default:
case NS_STYLE_VERTICAL_ALIGN_BASELINE:
// Align the kid's baseline at the max baseline
kidYTop = aMaxAscent - kidAscent;
break;
case NS_STYLE_VERTICAL_ALIGN_TOP:
// Align the top of the kid with the top of the line box
break;
case NS_STYLE_VERTICAL_ALIGN_SUB:
// Align the child's baseline on the superscript baseline
fm->GetSubscriptOffset(fontParam);
kidYTop = aMaxAscent + fontParam - kidAscent;
break;
case NS_STYLE_VERTICAL_ALIGN_SUPER:
// Align the child's baseline on the subscript baseline
fm->GetSuperscriptOffset(fontParam);
kidYTop = aMaxAscent - fontParam - kidAscent;
break;
case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
pass2Kids++;
isPass2Kid = PR_TRUE;
break;
case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
// Align the midpoint of the box with 1/2 the parent's x-height
fm->GetXHeight(fontParam);
kidYTop = aMaxAscent - (fontParam / 2) - (kidRect.height/2);
break;
case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM:
fm->GetMaxDescent(fontParam);
kidYTop = aMaxAscent + fontParam - kidRect.height;
break;
case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP:
fm->GetMaxAscent(fontParam);
kidYTop = aMaxAscent - fontParam;
break;
}
break;
default:
// Align the kid's baseline at the max baseline
kidYTop = aMaxAscent - kidAscent;
break;
}
/* XXX or grow the box - which is it? */
if (kidYTop < 0) {
kidYTop = 0;
}
// Place kid and update min and max Y values
if (!isPass2Kid) {
nscoord y = y0 + kidYTop;
if (y < minY) minY = y;
kid->MoveTo(kidRect.x, y);
y += kidRect.height;
if (y > maxY) maxY = y;
}
else {
nscoord y = y0 + kidRect.height;
if (y > maxY) maxY = y;
}
kid->GetNextSibling(kid);
}
// Now compute the final line-height
nscoord lineHeight = maxY - minY;
if (0 != pass2Kids) {
// Position all of the bottom aligned children
kidCount = mFrameNum;
kid = mFirstFrame;
pfd = mFrameData;
for (; --kidCount >= 0; pfd++) {
nscoord kidAscent = pfd->mAscent;
// Get kid's vertical align style data
const nsStyleText* textStyle;
kid->GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&)textStyle);
nsStyleUnit verticalAlignUnit = textStyle->mVerticalAlign.GetUnit();
if (eStyleUnit_Percent == verticalAlignUnit) {
// According to the spec, a positive value "raises" the box by
// the given distance while a negative value "lowers" the box
// by the given distance. Since Y coordinates increase towards
// the bottom of the screen we reverse the sign. All of the
// raising and lowering is done relative to the baseline, so
// we start our adjustments there.
nscoord kidYTop = aMaxAscent - kidAscent; // get baseline first
kidYTop -=
nscoord(textStyle->mVerticalAlign.GetPercentValue() * lineHeight);
kid->GetRect(kidRect);
kid->MoveTo(kidRect.x, y0 + kidYTop);
if (--pass2Kids == 0) {
// Stop on last pass2 kid
break;
}
}
else if (verticalAlignUnit == eStyleUnit_Enumerated) {
PRUint8 verticalAlignEnum = textStyle->mVerticalAlign.GetIntValue();
// Vertically align the child
if (NS_STYLE_VERTICAL_ALIGN_BOTTOM == verticalAlignEnum) {
// Place kid along the bottom
kid->GetRect(kidRect);
kid->MoveTo(kidRect.x, y0 + lineHeight - kidRect.height);
if (--pass2Kids == 0) {
// Stop on last pass2 kid
break;
}
}
}
kid->GetNextSibling(kid);
}
}
NS_RELEASE(fm);
} }
aLineBox.x = x; // Get the parent elements font in case we need it
aLineBox.y = y0; const nsStyleFont* font;
aLineBox.width = width; mOuterFrame->GetStyleData(eStyleStruct_Font,
aLineBox.height = height; (const nsStyleStruct*&)font);
nsIFontMetrics* fm = mPresContext.GetMetricsFor(font->mFont);
// Examine each and determine the minYTop, the maxYBottom and the
// maximum height. We will use these values to determine the final
// height of the line box and then position each frame.
nscoord minYTop = 0;
nscoord maxYBottom = 0;
nscoord maxHeight = 0;
PRBool haveTBFrames = PR_FALSE;
PerFrameData* pfd;
for (pfd = pfd0; pfd < end; pfd++) {
PRUint8 verticalAlignEnum;
nscoord fontParam;
nsIFrame* frame = pfd->mFrame;
// yTop = Y coordinate for the top of frame box <B>relative</B> to
// the baseline of the linebox which is assumed to be at Y=0
nscoord yTop;
// Compute the effective height of the box applying the top and
// bottom margins
nscoord height = pfd->mBounds.height + pfd->mMargin.top +
pfd->mMargin.bottom;
if (height > maxHeight) {
maxHeight = pfd->mBounds.height;
}
pfd->mAscent += pfd->mMargin.top;
const nsStyleText* textStyle;
frame->GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&)textStyle);
nsStyleUnit verticalAlignUnit = textStyle->mVerticalAlign.GetUnit();
switch (verticalAlignUnit) {
case eStyleUnit_Enumerated:
verticalAlignEnum = textStyle->mVerticalAlign.GetIntValue();
switch (verticalAlignEnum) {
default:
case NS_STYLE_VERTICAL_ALIGN_BASELINE:
yTop = -pfd->mAscent;
break;
case NS_STYLE_VERTICAL_ALIGN_SUB:
// Align the frames baseline on the subscript baseline
fm->GetSubscriptOffset(fontParam);
yTop = fontParam - pfd->mAscent;
break;
case NS_STYLE_VERTICAL_ALIGN_SUPER:
// Align the frames baseline on the superscript baseline
fm->GetSuperscriptOffset(fontParam);
yTop = -fontParam - pfd->mAscent;
break;
case NS_STYLE_VERTICAL_ALIGN_TOP:
case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
// THESE ARE DONE DURING PASS2
haveTBFrames = PR_TRUE;
continue;
case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
// Align the midpoint of the frame with 1/2 the parents x-height
fm->GetXHeight(fontParam);
yTop = -(fontParam / 2) - (pfd->mBounds.height/2);
break;
case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM:
fm->GetMaxDescent(fontParam);
yTop = fontParam - pfd->mBounds.height;
break;
case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP:
fm->GetMaxAscent(fontParam);
yTop = -fontParam;
break;
}
break;
case eStyleUnit_Coord:
// According to the CSS2 spec (10.8.1), a positive value
// "raises" the box by the given distance while a negative value
// "lowers" the box by the given distance. Since Y coordinates
// increase towards the bottom of the screen we reverse the
// sign. All of the raising and lowering is done relative to the
// baseline, so we start our adjustments there.
yTop = -pfd->mAscent - textStyle->mVerticalAlign.GetCoordValue();
break;
case eStyleUnit_Percent:
// The percentage is relative to the line-height of the element
// itself. The line-height will be the final height of the
// inline element (CSS2 10.8.1 says that the line-height defines
// the precise height of inline non-replaced elements).
yTop = -pfd->mAscent -
nscoord(textStyle->mVerticalAlign.GetPercentValue() * pfd->mBounds.height);
break;
default:
yTop = -pfd->mAscent;
break;
}
pfd->mBounds.y = yTop;
if (yTop < minYTop) {
minYTop = yTop;
}
// yBottom = Y coordinate for the bottom of the frame box, again
// relative to the baseline where Y=0
nscoord yBottom = yTop + height;
if (yBottom > maxYBottom) {
maxYBottom = yBottom;
}
}
// Once we have finished the above abs(minYTop) represents the
// maximum ascent of the line box.
// XXX what about positive minY?
nscoord lineHeight = maxYBottom - minYTop;
if (lineHeight < maxHeight) {
// This ensures that any object aligned top/bottom will update the
// line height properly since they don't impact the minY or
// maxYBottom values.
lineHeight = maxHeight;
}
aLineBox.height = lineHeight;
nscoord maxAscent = -minYTop;
// Pass2 - position each of the frames
for (pfd = pfd0; pfd < end; pfd++) {
nsIFrame* frame = pfd->mFrame;
const nsStyleText* textStyle;
frame->GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&)textStyle);
nsStyleUnit verticalAlignUnit = textStyle->mVerticalAlign.GetUnit();
if (eStyleUnit_Enumerated == verticalAlignUnit) {
PRUint8 verticalAlignEnum = textStyle->mVerticalAlign.GetIntValue();
switch (verticalAlignEnum) {
case NS_STYLE_VERTICAL_ALIGN_TOP:
// XXX negative top margins on these will do weird things, maybe?
pfd->mBounds.y = mTopEdge + pfd->mMargin.top;
break;
case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
pfd->mBounds.y = mTopEdge + lineHeight - pfd->mBounds.height;
break;
default:
pfd->mBounds.y = mTopEdge + maxAscent + pfd->mBounds.y -
pfd->mMargin.top;
break;
}
}
else {
pfd->mBounds.y = mTopEdge + maxAscent + pfd->mBounds.y -
pfd->mMargin.top;
}
frame->SetRect(pfd->mBounds);
}
aMaxAscent = maxAscent;
aMaxDescent = lineHeight - maxAscent;
// XXX Now we can apply 1/2 the line-height...
NS_RELEASE(fm);
} }
void void
nsInlineReflow::HorizontalAlignFrames(const nsRect& aLineBox) nsInlineReflow::HorizontalAlignFrames(const nsRect& aLineBox)
{ {
const nsStyleText* styleText = mOuterReflowState.mStyleText; const nsStyleText* styleText = mOuterReflowState.mStyleText;
nsCSSLayout::HorizontallyPlaceChildren(&mPresContext, nscoord maxWidth = mRightEdge - mLeftEdge;
mOuterFrame, if (aLineBox.width < maxWidth) {
styleText->mTextAlign, nscoord dx = 0;
mOuterReflowState.mDirection, switch (styleText->mTextAlign) {
mFirstFrame, mFrameNum, case NS_STYLE_TEXT_ALIGN_DEFAULT:
aLineBox.width, if (NS_STYLE_DIRECTION_LTR == mOuterReflowState.mDirection) {
mRightEdge - mLeftEdge); // default alignment for left-to-right is left so do nothing
return;
}
// Fall through to align right case for default alignment
// used when the direction is right-to-left.
case NS_STYLE_TEXT_ALIGN_RIGHT:
dx = maxWidth - aLineBox.width;
break;
case NS_STYLE_TEXT_ALIGN_LEFT:
case NS_STYLE_TEXT_ALIGN_JUSTIFY:
// Default layout has everything aligned left
return;
case NS_STYLE_TEXT_ALIGN_CENTER:
dx = (maxWidth - aLineBox.width) / 2;
break;
}
// Position children
PerFrameData* pfd = mFrameDataBase;
PerFrameData* end = pfd + mFrameNum;
nsPoint origin;
for (; pfd < end; pfd++) {
nsIFrame* kid = pfd->mFrame;;
kid->GetOrigin(origin);
kid->MoveTo(origin.x + dx, origin.y);
kid->GetNextSibling(kid);
}
}
} }
void void
nsInlineReflow::RelativePositionFrames() nsInlineReflow::RelativePositionFrames()
{ {
nsCSSLayout::RelativePositionChildren(&mPresContext, nsPoint origin;
mOuterFrame, PerFrameData* pfd = mFrameDataBase;
mFirstFrame, mFrameNum);
}
void
nsInlineReflow::GetScents(nscoord& aMaxAscent, nscoord& aMaxDescent)
{
PerFrameData* pfd = mFrameData;
PerFrameData* end = pfd + mFrameNum; PerFrameData* end = pfd + mFrameNum;
nscoord maxAscent = 0; for (; pfd < end; pfd++) {
nscoord maxDescent = 0; nsIFrame* kid = pfd->mFrame;
while (pfd < end) { const nsStylePosition* kidPosition;
if (pfd->mAscent > maxAscent) maxAscent = pfd->mAscent; kid->GetStyleData(eStyleStruct_Position,
if (pfd->mDescent > maxDescent) maxDescent = pfd->mDescent; (const nsStyleStruct*&)kidPosition);
pfd++; if (NS_STYLE_POSITION_RELATIVE == kidPosition->mPosition) {
kid->GetOrigin(origin);
nscoord dx = 0;
switch (kidPosition->mLeftOffset.GetUnit()) {
case eStyleUnit_Percent:
printf("XXX: not yet implemented: % relative position\n");
case eStyleUnit_Auto:
break;
case eStyleUnit_Coord:
dx = kidPosition->mLeftOffset.GetCoordValue();
break;
}
nscoord dy = 0;
switch (kidPosition->mTopOffset.GetUnit()) {
case eStyleUnit_Percent:
printf("XXX: not yet implemented: % relative position\n");
case eStyleUnit_Auto:
break;
case eStyleUnit_Coord:
dy = kidPosition->mTopOffset.GetCoordValue();
break;
}
kid->MoveTo(origin.x + dx, origin.y + dy);
}
} }
aMaxAscent = maxAscent;
aMaxDescent = maxDescent;
} }

Просмотреть файл

@ -64,8 +64,6 @@ public:
void RelativePositionFrames(); void RelativePositionFrames();
void GetScents(nscoord& aMaxAscent, nscoord& aMaxDescent);
PRInt32 GetCurrentFrameNum() const { return mFrameNum; } PRInt32 GetCurrentFrameNum() const { return mFrameNum; }
void ChangeFrameCount(PRInt32 aCount) { void ChangeFrameCount(PRInt32 aCount) {
@ -83,11 +81,17 @@ public:
nscoord GetCarriedOutMarginFlags() const { return mCarriedOutMarginFlags; } nscoord GetCarriedOutMarginFlags() const { return mCarriedOutMarginFlags; }
nscoord GetTopMargin() const { return mMargin.top; } nscoord GetTopMargin() const {
return mFrameData->mMargin.top;
}
nscoord GetBottomMargin() const { return mMargin.bottom; } nscoord GetBottomMargin() const {
return mFrameData->mMargin.bottom;
}
PRUintn GetMarginFlags() const { return mMarginFlags; } PRUintn GetMarginFlags() const {
return mFrameData->mMarginFlags;
}
static PRUintn CalculateBlockMarginsFor(nsIPresContext& aPresContext, static PRUintn CalculateBlockMarginsFor(nsIPresContext& aPresContext,
nsIFrame* aFrame, nsIFrame* aFrame,
@ -101,7 +105,7 @@ public:
static nscoord MaxMargin(nscoord a, nscoord b); static nscoord MaxMargin(nscoord a, nscoord b);
protected: protected:
void SetFrame(nsIFrame* aFrame); nsresult SetFrame(nsIFrame* aFrame);
PRBool TreatFrameAsBlockFrame(); PRBool TreatFrameAsBlockFrame();
@ -127,8 +131,6 @@ protected:
void PlaceFrame(nsHTMLReflowMetrics& aMetrics, nsRect& aBounds); void PlaceFrame(nsHTMLReflowMetrics& aMetrics, nsRect& aBounds);
nsresult SetFrameData(const nsHTMLReflowMetrics& aMetrics);
void UpdateFrames(); void UpdateFrames();
// The outer frame that contains the frames that we reflow. // The outer frame that contains the frames that we reflow.
@ -139,21 +141,33 @@ protected:
nsIPresContext& mPresContext; nsIPresContext& mPresContext;
PRBool mOuterIsBlock; PRBool mOuterIsBlock;
nsIFrame* mFirstFrame;
PRIntn mFrameNum; PRIntn mFrameNum;
/*
* For each frame reflowed, we keep this state around
*/
struct PerFrameData { struct PerFrameData {
nsIFrame* mFrame;
nscoord mAscent; // computed ascent value nscoord mAscent; // computed ascent value
nscoord mDescent; // computed descent value nscoord mDescent; // computed descent value
nsMargin mMargin; // computed margin value nsMargin mMargin; // computed margin value
PRUintn mMarginFlags;
// Location and size of frame after its reflowed but before it is
// positioned finally by VerticalAlignFrames
nsRect mBounds;
nsSize mMaxElementSize;
}; };
PerFrameData* mFrameData; PerFrameData* mFrameData;
PerFrameData* mFrameDataBase;
PerFrameData mFrameDataBuf[20]; PerFrameData mFrameDataBuf[20];
PRIntn mNumFrameData; PRIntn mNumFrameData;
// Current frame state // Current frame state
nsIFrame* mFrame;
const nsStyleSpacing* mSpacing; const nsStyleSpacing* mSpacing;
const nsStylePosition* mPosition; const nsStylePosition* mPosition;
const nsStyleDisplay* mDisplay; const nsStyleDisplay* mDisplay;
@ -174,8 +188,6 @@ protected:
// The frame's computed margin values (includes auto value // The frame's computed margin values (includes auto value
// computation) // computation)
nsMargin mMargin;
PRUintn mMarginFlags;
nscoord mRightMargin;/* XXX */ nscoord mRightMargin;/* XXX */
nscoord mCarriedOutTopMargin; nscoord mCarriedOutTopMargin;
nscoord mCarriedOutBottomMargin; nscoord mCarriedOutBottomMargin;
@ -190,7 +202,6 @@ protected:
nscoord mRightEdge; nscoord mRightEdge;
nscoord mTopEdge; nscoord mTopEdge;
nscoord mY;
nscoord mBottomEdge; nscoord mBottomEdge;
PRBool mUpdatedBand; PRBool mUpdatedBand;