From ad7b8e18645f479552277a4c6f45404f7249d62c Mon Sep 17 00:00:00 2001 From: "kipp%netscape.com" Date: Tue, 27 Oct 1998 16:52:10 +0000 Subject: [PATCH] Implement justification methods --- layout/generic/nsInlineFrame.cpp | 157 ++++++++++++++++++++++++ layout/generic/nsLineLayout.h | 14 +-- layout/html/base/src/nsInlineFrame.cpp | 157 ++++++++++++++++++++++++ layout/html/base/src/nsInlineReflow.cpp | 126 +++++++++++++++++-- layout/html/base/src/nsInlineReflow.h | 7 +- layout/html/base/src/nsLineLayout.h | 14 +-- 6 files changed, 442 insertions(+), 33 deletions(-) diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp index d934f726f1cd..a36940f76610 100644 --- a/layout/generic/nsInlineFrame.cpp +++ b/layout/generic/nsInlineFrame.cpp @@ -74,6 +74,10 @@ public: nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus); + NS_IMETHOD AdjustFrameSize(nscoord aExtraSpace, nscoord& aUsedSpace); + NS_IMETHOD TrimTrailingWhiteSpace(nsIPresContext& aPresContext, + nsIRenderingContext& aRC, + nscoord& aDeltaWidth); virtual PRIntn GetSkipSides() const; @@ -283,6 +287,159 @@ nsInlineFrame::FindTextRuns(nsLineLayout& aLineLayout) return rv; } +// XXX This code is *almost* identical to the code in +// nsInlineReflow::JustifyFrames; factor it somehow +NS_IMETHODIMP +nsInlineFrame::AdjustFrameSize(nscoord aExtraSpace, nscoord& aUsedSpace) +{ + if (0 >= aExtraSpace) { + aUsedSpace = 0; + return NS_OK; + } + + struct AdjustData { + nsIFrame* frame; + PRBool splittable; + nsRect bounds; + }; + const int NUM_AD = 50; + AdjustData adjustMem[NUM_AD]; + AdjustData* ad0 = adjustMem; + AdjustData* end = ad0 + NUM_AD; + AdjustData* ad = ad0; + PRInt32 numAD = NUM_AD; + + // Gather up raw data for justification + nsIFrame* frame = mFirstChild; + PRInt32 fixed = 0; + PRInt32 total = 0; + nscoord fixedWidth = 0; + for (; nsnull != frame; ad++, total++) { + // Grow temporary storage if we have to + if (ad == end) { + AdjustData* newAD = new AdjustData[numAD + numAD]; + if (nsnull == newAD) { + if (ad0 != adjustMem) { + delete [] ad0; + } + aUsedSpace = 0; + return NS_OK; + } + nsCRT::memcpy(newAD, ad0, sizeof(AdjustData) * numAD); + ad = newAD + (ad - ad0); + if (ad0 != adjustMem) { + delete [] ad0; + } + ad0 = newAD; + end = ad0 + numAD; + numAD = numAD + numAD; + } + + // Record info about the frame + ad->frame = frame; + frame->GetRect(ad->bounds); + nsSplittableType isSplittable = NS_FRAME_NOT_SPLITTABLE; + frame->IsSplittable(isSplittable); + if ((0 == ad->bounds.width) || + NS_FRAME_IS_NOT_SPLITTABLE(isSplittable)) { + ad->splittable = PR_FALSE; + fixed++; + fixedWidth += ad->bounds.width; + } + else { + ad->splittable = PR_TRUE; + } + + // Advance to the next frame + frame->GetNextSibling(frame); + } + + nscoord totalUsed = 0; + nscoord variableWidth = mRect.width - fixedWidth; + if (variableWidth > 0) { + // Each variable width frame gets a portion of the available extra + // space that is proportional to the space it takes in the + // line. The extra space is given to the frame by updating its + // position and size. The frame is responsible for adjusting the + // position of its contents on its own (during rendering). + PRInt32 i, splittable = total - fixed; + nscoord extraSpace = aExtraSpace; + nscoord remainingExtra = extraSpace; + nscoord dx = 0; + float lineWidth = float(mRect.width); + ad = ad0; + for (i = 0; i < total; i++, ad++) { + nsIFrame* frame = ad->frame; + nsIHTMLReflow* ihr; + if (NS_OK == frame->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) { + nsRect r; + if (ad->splittable && (ad->bounds.width > 0)) { + float pctOfLine = float(ad->bounds.width) / lineWidth; + nscoord extra = nscoord(pctOfLine * extraSpace); + if (--splittable == 0) { + extra = remainingExtra; + } + if (0 != extra) { + nscoord used; + ihr->AdjustFrameSize(extra, used); + if (used < extra) { + frame->ListTag(); printf(": extra=%d used=%d\n", extra, used); + } + totalUsed += used; + frame->GetRect(r); + r.x += dx; + frame->SetRect(r); + dx += used; + remainingExtra -= used; + } + else if (0 != dx) { + frame->GetRect(r); + r.x += dx; + frame->SetRect(r); + } + } + else if (0 != dx) { + frame->GetRect(r); + r.x += dx; + frame->SetRect(r); + } + } + } + } + mRect.width += totalUsed; + aUsedSpace = totalUsed; + + if (ad0 != adjustMem) { + delete [] ad0; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsInlineFrame::TrimTrailingWhiteSpace(nsIPresContext& aPresContext, + nsIRenderingContext& aRC, + nscoord& aDeltaWidth) +{ + nsIFrame* lastFrame = LastFrame(mFirstChild); + if (nsnull == lastFrame) { + aDeltaWidth = 0; + } + else { + nsIHTMLReflow* ihr; + if (NS_OK == lastFrame->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) { + ihr->TrimTrailingWhiteSpace(aPresContext, aRC, aDeltaWidth); + if (0 != aDeltaWidth) { + mRect.width -= aDeltaWidth; + } + } + else { + aDeltaWidth = 0; + } + } + return NS_OK; +} + nsresult nsInlineFrame::InsertNewFrame(nsIPresContext& aPresContext, nsIFrame* aNewFrame, diff --git a/layout/generic/nsLineLayout.h b/layout/generic/nsLineLayout.h index a88eb7b308ea..ed9d7b665573 100644 --- a/layout/generic/nsLineLayout.h +++ b/layout/generic/nsLineLayout.h @@ -50,29 +50,25 @@ public: mColumn = 0; mSkipLeadingWS = PR_TRUE; mBRFrame = nsnull; -#ifdef NS_DEBUG mPlacedFrames.Clear(); -#endif ForgetWordFrames(); } // Add to the placed-frame count -#ifdef NS_DEBUG void AddPlacedFrame(nsIFrame* aFrame) { mTotalPlacedFrames++; mPlacedFrames.AppendElement(aFrame); } -#else - void AddPlacedFrame() { - mTotalPlacedFrames++; - } -#endif // Get the placed-frame count PRInt32 GetPlacedFrames() const { return mTotalPlacedFrames; } + const nsVoidArray& PlacedFrames() const { + return mPlacedFrames; + } + void SetBRFrame(nsIFrame* aFrame) { mBRFrame = aFrame; } @@ -180,9 +176,7 @@ protected: nsIFrame* mBRFrame; PRInt32 mTotalPlacedFrames; -#ifdef NS_DEBUG nsVoidArray mPlacedFrames; -#endif nsVoidArray mWordFrames; diff --git a/layout/html/base/src/nsInlineFrame.cpp b/layout/html/base/src/nsInlineFrame.cpp index d934f726f1cd..a36940f76610 100644 --- a/layout/html/base/src/nsInlineFrame.cpp +++ b/layout/html/base/src/nsInlineFrame.cpp @@ -74,6 +74,10 @@ public: nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus); + NS_IMETHOD AdjustFrameSize(nscoord aExtraSpace, nscoord& aUsedSpace); + NS_IMETHOD TrimTrailingWhiteSpace(nsIPresContext& aPresContext, + nsIRenderingContext& aRC, + nscoord& aDeltaWidth); virtual PRIntn GetSkipSides() const; @@ -283,6 +287,159 @@ nsInlineFrame::FindTextRuns(nsLineLayout& aLineLayout) return rv; } +// XXX This code is *almost* identical to the code in +// nsInlineReflow::JustifyFrames; factor it somehow +NS_IMETHODIMP +nsInlineFrame::AdjustFrameSize(nscoord aExtraSpace, nscoord& aUsedSpace) +{ + if (0 >= aExtraSpace) { + aUsedSpace = 0; + return NS_OK; + } + + struct AdjustData { + nsIFrame* frame; + PRBool splittable; + nsRect bounds; + }; + const int NUM_AD = 50; + AdjustData adjustMem[NUM_AD]; + AdjustData* ad0 = adjustMem; + AdjustData* end = ad0 + NUM_AD; + AdjustData* ad = ad0; + PRInt32 numAD = NUM_AD; + + // Gather up raw data for justification + nsIFrame* frame = mFirstChild; + PRInt32 fixed = 0; + PRInt32 total = 0; + nscoord fixedWidth = 0; + for (; nsnull != frame; ad++, total++) { + // Grow temporary storage if we have to + if (ad == end) { + AdjustData* newAD = new AdjustData[numAD + numAD]; + if (nsnull == newAD) { + if (ad0 != adjustMem) { + delete [] ad0; + } + aUsedSpace = 0; + return NS_OK; + } + nsCRT::memcpy(newAD, ad0, sizeof(AdjustData) * numAD); + ad = newAD + (ad - ad0); + if (ad0 != adjustMem) { + delete [] ad0; + } + ad0 = newAD; + end = ad0 + numAD; + numAD = numAD + numAD; + } + + // Record info about the frame + ad->frame = frame; + frame->GetRect(ad->bounds); + nsSplittableType isSplittable = NS_FRAME_NOT_SPLITTABLE; + frame->IsSplittable(isSplittable); + if ((0 == ad->bounds.width) || + NS_FRAME_IS_NOT_SPLITTABLE(isSplittable)) { + ad->splittable = PR_FALSE; + fixed++; + fixedWidth += ad->bounds.width; + } + else { + ad->splittable = PR_TRUE; + } + + // Advance to the next frame + frame->GetNextSibling(frame); + } + + nscoord totalUsed = 0; + nscoord variableWidth = mRect.width - fixedWidth; + if (variableWidth > 0) { + // Each variable width frame gets a portion of the available extra + // space that is proportional to the space it takes in the + // line. The extra space is given to the frame by updating its + // position and size. The frame is responsible for adjusting the + // position of its contents on its own (during rendering). + PRInt32 i, splittable = total - fixed; + nscoord extraSpace = aExtraSpace; + nscoord remainingExtra = extraSpace; + nscoord dx = 0; + float lineWidth = float(mRect.width); + ad = ad0; + for (i = 0; i < total; i++, ad++) { + nsIFrame* frame = ad->frame; + nsIHTMLReflow* ihr; + if (NS_OK == frame->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) { + nsRect r; + if (ad->splittable && (ad->bounds.width > 0)) { + float pctOfLine = float(ad->bounds.width) / lineWidth; + nscoord extra = nscoord(pctOfLine * extraSpace); + if (--splittable == 0) { + extra = remainingExtra; + } + if (0 != extra) { + nscoord used; + ihr->AdjustFrameSize(extra, used); + if (used < extra) { + frame->ListTag(); printf(": extra=%d used=%d\n", extra, used); + } + totalUsed += used; + frame->GetRect(r); + r.x += dx; + frame->SetRect(r); + dx += used; + remainingExtra -= used; + } + else if (0 != dx) { + frame->GetRect(r); + r.x += dx; + frame->SetRect(r); + } + } + else if (0 != dx) { + frame->GetRect(r); + r.x += dx; + frame->SetRect(r); + } + } + } + } + mRect.width += totalUsed; + aUsedSpace = totalUsed; + + if (ad0 != adjustMem) { + delete [] ad0; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsInlineFrame::TrimTrailingWhiteSpace(nsIPresContext& aPresContext, + nsIRenderingContext& aRC, + nscoord& aDeltaWidth) +{ + nsIFrame* lastFrame = LastFrame(mFirstChild); + if (nsnull == lastFrame) { + aDeltaWidth = 0; + } + else { + nsIHTMLReflow* ihr; + if (NS_OK == lastFrame->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) { + ihr->TrimTrailingWhiteSpace(aPresContext, aRC, aDeltaWidth); + if (0 != aDeltaWidth) { + mRect.width -= aDeltaWidth; + } + } + else { + aDeltaWidth = 0; + } + } + return NS_OK; +} + nsresult nsInlineFrame::InsertNewFrame(nsIPresContext& aPresContext, nsIFrame* aNewFrame, diff --git a/layout/html/base/src/nsInlineReflow.cpp b/layout/html/base/src/nsInlineReflow.cpp index fd24ae64a28f..bd6977b8279e 100644 --- a/layout/html/base/src/nsInlineReflow.cpp +++ b/layout/html/base/src/nsInlineReflow.cpp @@ -880,8 +880,24 @@ nsInlineReflow::VerticalAlignFrames(nsRect& aLineBox, } void -nsInlineReflow::HorizontalAlignFrames(const nsRect& aLineBox) +nsInlineReflow::HorizontalAlignFrames(nsRect& aLineBox, PRBool aIsLastLine) { + // Before we start, trim any trailing whitespace off of the last + // frame in the line. + nscoord deltaWidth; + PerFrameData* pfd = mFrameDataBase + (mFrameNum - 1); + if (pfd->mBounds.width > 0) { + nsIFrame* frame = pfd->mFrame; + nsIHTMLReflow* ihr; + if (NS_OK == frame->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) { + ihr->TrimTrailingWhiteSpace(mPresContext, + *mOuterReflowState.rendContext, + deltaWidth); + aLineBox.width -= deltaWidth; + pfd->mBounds.width -= deltaWidth; + } + } + const nsStyleText* styleText = mOuterReflowState.mStyleText; nscoord maxWidth = mRightEdge - mLeftEdge; if (aLineBox.width < maxWidth) { @@ -900,24 +916,38 @@ nsInlineReflow::HorizontalAlignFrames(const nsRect& aLineBox) 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_JUSTIFY: + // If this is not the last line then go ahead and justify the + // frames in the line. If it is the last line then if the + // direction is right-to-left then we right-align the frames. + if (!aIsLastLine) { + JustifyFrames(maxWidth, aLineBox); + return; + } + else if (NS_STYLE_DIRECTION_RTL == mOuterReflowState.mDirection) { + // right align the frames + dx = maxWidth - aLineBox.width; + } + break; + 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); + if (0 != dx) { + // Position children + 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); + } } } } @@ -1032,3 +1062,75 @@ nsInlineReflow::CalcLineHeightFor(nsIPresContext& aPresContext, return lineHeight; } + +void +nsInlineReflow::JustifyFrames(nscoord aMaxWidth, nsRect& aLineBox) +{ + // Gather up raw data for justification + PRInt32 i, n = mFrameNum; + PerFrameData* pfd = mFrameDataBase; + PRInt32 fixed = 0; + nscoord fixedWidth = 0; + for (i = 0; i < n; i++, pfd++) { + nsIFrame* frame = pfd->mFrame; + nsSplittableType isSplittable = NS_FRAME_NOT_SPLITTABLE; + frame->IsSplittable(isSplittable); + if ((0 == pfd->mBounds.width) || + NS_FRAME_IS_NOT_SPLITTABLE(isSplittable)) { + pfd->mSplittable = PR_FALSE; + fixed++; + fixedWidth += pfd->mBounds.width; + } + else { + pfd->mSplittable = PR_TRUE; + } + } + + nscoord variableWidth = aLineBox.width - fixedWidth; + if (variableWidth > 0) { + // Each variable width frame gets a portion of the available extra + // space that is proportional to the space it takes in the + // line. The extra space is given to the frame by updating its + // position and size. The frame is responsible for adjusting the + // position of its contents on its own (during rendering). + PRInt32 splittable = n - fixed; + nscoord extraSpace = aMaxWidth - aLineBox.width; + nscoord remainingExtra = extraSpace; + nscoord dx = 0; + float lineWidth = float(aLineBox.width); + pfd = mFrameDataBase; + for (i = 0; i < n; i++, pfd++) { + nsRect r; + nsIFrame* frame = pfd->mFrame; + nsIHTMLReflow* ihr; + if (NS_OK == frame->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) { + if (pfd->mSplittable && (pfd->mBounds.width > 0)) { + float pctOfLine = float(pfd->mBounds.width) / lineWidth; + nscoord extra = nscoord(pctOfLine * extraSpace); + if (--splittable == 0) { + extra = remainingExtra; + } + if (0 != extra) { + nscoord used; + ihr->AdjustFrameSize(extra, used); + frame->GetRect(r); + r.x += dx; + frame->SetRect(r); + dx += extra; + } + else if (0 != dx) { + frame->GetRect(r); + r.x += dx; + frame->SetRect(r); + } + remainingExtra -= extra; + } + else if (0 != dx) { + frame->GetRect(r); + r.x += dx; + frame->SetRect(r); + } + } + } + } +} diff --git a/layout/html/base/src/nsInlineReflow.h b/layout/html/base/src/nsInlineReflow.h index 47d1af57b532..5c9d1959b14a 100644 --- a/layout/html/base/src/nsInlineReflow.h +++ b/layout/html/base/src/nsInlineReflow.h @@ -60,7 +60,8 @@ public: nscoord& aMaxAscent, nscoord& aMaxDescent); - void HorizontalAlignFrames(const nsRect& aLineBox); + void HorizontalAlignFrames(nsRect& aLineBox, + PRBool aIsLastLine = PR_FALSE); void RelativePositionFrames(); @@ -138,6 +139,8 @@ protected: void UpdateFrames(); + void JustifyFrames(nscoord aMaxWidth, nsRect& aLineBox); + // The outer frame that contains the frames that we reflow. nsHTMLContainerFrame* mOuterFrame; nsISpaceManager* mSpaceManager; @@ -165,6 +168,8 @@ protected: nsRect mBounds; nsSize mMaxElementSize; + + PRBool mSplittable; }; PerFrameData* mFrameData; diff --git a/layout/html/base/src/nsLineLayout.h b/layout/html/base/src/nsLineLayout.h index a88eb7b308ea..ed9d7b665573 100644 --- a/layout/html/base/src/nsLineLayout.h +++ b/layout/html/base/src/nsLineLayout.h @@ -50,29 +50,25 @@ public: mColumn = 0; mSkipLeadingWS = PR_TRUE; mBRFrame = nsnull; -#ifdef NS_DEBUG mPlacedFrames.Clear(); -#endif ForgetWordFrames(); } // Add to the placed-frame count -#ifdef NS_DEBUG void AddPlacedFrame(nsIFrame* aFrame) { mTotalPlacedFrames++; mPlacedFrames.AppendElement(aFrame); } -#else - void AddPlacedFrame() { - mTotalPlacedFrames++; - } -#endif // Get the placed-frame count PRInt32 GetPlacedFrames() const { return mTotalPlacedFrames; } + const nsVoidArray& PlacedFrames() const { + return mPlacedFrames; + } + void SetBRFrame(nsIFrame* aFrame) { mBRFrame = aFrame; } @@ -180,9 +176,7 @@ protected: nsIFrame* mBRFrame; PRInt32 mTotalPlacedFrames; -#ifdef NS_DEBUG nsVoidArray mPlacedFrames; -#endif nsVoidArray mWordFrames;