Bug 544852, part 4: Add nsCaret::GetGeometryForFrame. r=roc

This commit is contained in:
Mats Palmgren 2010-03-14 23:56:26 +01:00
parent a445269db2
commit 355d7fd5b3
2 changed files with 65 additions and 58 deletions

View File

@ -357,6 +357,57 @@ void nsCaret::SetCaretReadOnly(PRBool inMakeReadonly)
mReadOnly = inMakeReadonly;
}
void
nsCaret::GetGeometryForFrame(nsIFrame* aFrame,
PRInt32 aFrameOffset,
nsRect* aRect,
nscoord* aBidiIndicatorSize)
{
nsPoint framePos(0, 0);
aFrame->GetPointFromOffset(aFrameOffset, &framePos);
nscoord height = aFrame->GetContentRect().height;
if (height == 0) {
nsCOMPtr<nsIFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm));
if (fm) {
nscoord ascent, descent;
fm->GetMaxAscent(ascent);
fm->GetMaxDescent(descent);
height = ascent + descent;
// Place the caret on the baseline for inline frames, except when there is
// a frame on the line with non-zero height. XXXmats why the exception? --
// I don't know but it seems to be necessary, see bug 503531.
if (aFrame->GetStyleDisplay()->IsInlineOutside() &&
!FramesOnSameLineHaveZeroHeight(aFrame))
framePos.y -= ascent;
}
}
Metrics caretMetrics = ComputeMetrics(aFrame, aFrameOffset, height);
*aRect = nsRect(framePos, nsSize(caretMetrics.mCaretWidth, height));
// Clamp the x-position to be within our scroll frame. If we don't, then it
// clips us, and we don't appear at all. See bug 335560.
nsIFrame *scrollFrame =
nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::scrollFrame);
if (scrollFrame) {
// First, use the scrollFrame to get at the scrollable view that we're in.
nsIScrollableFrame *sf = do_QueryFrame(scrollFrame);
nsIFrame *scrolled = sf->GetScrolledFrame();
nsRect caretInScroll = *aRect + aFrame->GetOffsetTo(scrolled);
// Now see if thet caret extends beyond the view's bounds. If it does,
// then snap it back, put it as close to the edge as it can.
nscoord overflow = caretInScroll.XMost() -
scrolled->GetOverflowRectRelativeToSelf().width;
if (overflow > 0)
aRect->x -= overflow;
}
if (aBidiIndicatorSize)
*aBidiIndicatorSize = caretMetrics.mBidiIndicatorSize;
}
nsIFrame* nsCaret::GetGeometry(nsISelection* aSelection, nsRect* aRect,
nscoord* aBidiIndicatorSize)
{
@ -374,67 +425,20 @@ nsIFrame* nsCaret::GetGeometry(nsISelection* aSelection, nsRect* aRect,
if (!contentNode)
return nsnull;
// find the frame that contains the content node that has focus
nsIFrame* theFrame = nsnull;
PRInt32 theFrameOffset = 0;
nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection();
if (!frameSelection)
return nsnull;
PRUint8 bidiLevel = frameSelection->GetCaretBidiLevel();
nsIFrame* frame;
PRInt32 frameOffset;
rv = GetCaretFrameForNodeOffset(contentNode, focusOffset,
frameSelection->GetHint(), bidiLevel,
&theFrame, &theFrameOffset);
if (NS_FAILED(rv) || !theFrame)
return nsnull;
nsPoint framePos(0, 0);
rv = theFrame->GetPointFromOffset(theFrameOffset, &framePos);
if (NS_FAILED(rv))
&frame, &frameOffset);
if (NS_FAILED(rv) || !frame)
return nsnull;
nscoord height = theFrame->GetContentRect().height;
if (height == 0) {
nsCOMPtr<nsIFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(theFrame, getter_AddRefs(fm));
if (fm) {
nscoord ascent, descent;
fm->GetMaxAscent(ascent);
fm->GetMaxDescent(descent);
height = ascent + descent;
// Place the caret on the baseline for inline frames, except when there is
// a frame on the line with non-zero height. XXXmats why the exception? --
// I don't know but it seems to be necessary, see bug 503531.
if (theFrame->GetStyleDisplay()->IsInlineOutside() &&
!FramesOnSameLineHaveZeroHeight(theFrame))
framePos.y -= ascent;
}
}
Metrics caretMetrics = ComputeMetrics(theFrame, theFrameOffset, height);
*aRect = nsRect(framePos, nsSize(caretMetrics.mCaretWidth, height));
// Clamp the x-position to be within our scroll frame. If we don't, then it
// clips us, and we don't appear at all. See bug 335560.
nsIFrame *scrollFrame =
nsLayoutUtils::GetClosestFrameOfType(theFrame, nsGkAtoms::scrollFrame);
if (scrollFrame) {
// First, use the scrollFrame to get at the scrolled frame that we're in.
nsIScrollableFrame *sf = do_QueryFrame(scrollFrame);
nsIFrame *scrolled = sf->GetScrolledFrame();
nsRect caretInScroll = *aRect + theFrame->GetOffsetTo(scrolled);
// Now see if thet caret extends beyond the frame's bounds. If it does,
// then snap it back, put it as close to the edge as it can.
nscoord overflow = caretInScroll.XMost() -
scrolled->GetOverflowRectRelativeToSelf().width;
if (overflow > 0)
aRect->x -= overflow;
}
if (aBidiIndicatorSize)
*aBidiIndicatorSize = caretMetrics.mBidiIndicatorSize;
return theFrame;
GetGeometryForFrame(frame, frameOffset, aRect, aBidiIndicatorSize);
return frame;
}
void nsCaret::DrawCaretAfterBriefDelay()
@ -1077,11 +1081,8 @@ nsCaret::UpdateCaretRects(nsIFrame* aFrame, PRInt32 aFrameOffset)
{
NS_ASSERTION(aFrame, "Should have a frame here");
nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
if (!domSelection)
return PR_FALSE;
nscoord bidiIndicatorSize;
GetGeometry(domSelection, &mCaretRect, &bidiIndicatorSize);
GetGeometryForFrame(aFrame, aFrameOffset, &mCaretRect, &bidiIndicatorSize);
// on RTL frames the right edge of mCaretRect must be equal to framePos
const nsStyleVisibility* vis = aFrame->GetStyleVisibility();
@ -1109,7 +1110,9 @@ nsCaret::UpdateCaretRects(nsIFrame* aFrame, PRInt32 aFrameOffset)
* without drawing the caret in the old position.
*/
mKeyboardRTL = isCaretRTL;
if (NS_SUCCEEDED(domSelection->SelectionLanguageChange(mKeyboardRTL)))
nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
if (!domSelection ||
NS_SUCCEEDED(domSelection->SelectionLanguageChange(mKeyboardRTL)))
return PR_FALSE;
}
// If keyboard language is RTL, draw the hook on the left; if LTR, to the right

View File

@ -215,6 +215,10 @@ protected:
nscoord mCaretWidth; // full caret width including bidi indicator
};
Metrics ComputeMetrics(nsIFrame* aFrame, PRInt32 aOffset, nscoord aCaretHeight);
void GetGeometryForFrame(nsIFrame* aFrame,
PRInt32 aFrameOffset,
nsRect* aRect,
nscoord* aBidiIndicatorSize);
// Returns true if the caret should be drawn. When |mDrawn| is true,
// this returns true, so that we erase the drawn caret. If |aIgnoreDrawnState|