зеркало из https://github.com/mozilla/pjs.git
Bug 544852, part 2: Use nsCaret::GetGeometry. r=roc
This commit is contained in:
Родитель
b05d82f7c5
Коммит
4d33c3d9c6
|
@ -81,6 +81,86 @@ static const PRInt32 kMinBidiIndicatorPixels = 2;
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#endif //IBMBIDI
|
#endif //IBMBIDI
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the first frame in an in-order traversal of the frame subtree rooted
|
||||||
|
* at aFrame which is either a text frame logically at the end of a line,
|
||||||
|
* or which is aStopAtFrame. Return null if no such frame is found. We don't
|
||||||
|
* descend into the children of non-eLineParticipant frames.
|
||||||
|
*/
|
||||||
|
static nsIFrame*
|
||||||
|
CheckForTrailingTextFrameRecursive(nsIFrame* aFrame, nsIFrame* aStopAtFrame)
|
||||||
|
{
|
||||||
|
if (aFrame == aStopAtFrame ||
|
||||||
|
((aFrame->GetType() == nsGkAtoms::textFrame &&
|
||||||
|
(static_cast<nsTextFrame*>(aFrame))->IsAtEndOfLine())))
|
||||||
|
return aFrame;
|
||||||
|
if (!aFrame->IsFrameOfType(nsIFrame::eLineParticipant))
|
||||||
|
return nsnull;
|
||||||
|
|
||||||
|
for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling())
|
||||||
|
{
|
||||||
|
nsIFrame* r = CheckForTrailingTextFrameRecursive(f, aStopAtFrame);
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
static nsLineBox*
|
||||||
|
FindContainingLine(nsIFrame* aFrame)
|
||||||
|
{
|
||||||
|
while (aFrame && aFrame->IsFrameOfType(nsIFrame::eLineParticipant))
|
||||||
|
{
|
||||||
|
nsIFrame* parent = aFrame->GetParent();
|
||||||
|
nsBlockFrame* blockParent = nsLayoutUtils::GetAsBlock(parent);
|
||||||
|
if (blockParent)
|
||||||
|
{
|
||||||
|
PRBool isValid;
|
||||||
|
nsBlockInFlowLineIterator iter(blockParent, aFrame, &isValid);
|
||||||
|
return isValid ? iter.GetLine().get() : nsnull;
|
||||||
|
}
|
||||||
|
aFrame = parent;
|
||||||
|
}
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
AdjustCaretFrameForLineEnd(nsIFrame** aFrame, PRInt32* aOffset)
|
||||||
|
{
|
||||||
|
nsLineBox* line = FindContainingLine(*aFrame);
|
||||||
|
if (!line)
|
||||||
|
return;
|
||||||
|
PRInt32 count = line->GetChildCount();
|
||||||
|
for (nsIFrame* f = line->mFirstChild; count > 0; --count, f = f->GetNextSibling())
|
||||||
|
{
|
||||||
|
nsIFrame* r = CheckForTrailingTextFrameRecursive(f, *aFrame);
|
||||||
|
if (r == *aFrame)
|
||||||
|
return;
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
*aFrame = r;
|
||||||
|
NS_ASSERTION(r->GetType() == nsGkAtoms::textFrame, "Expected text frame");
|
||||||
|
*aOffset = (static_cast<nsTextFrame*>(r))->GetContentEnd();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PRBool
|
||||||
|
FramesOnSameLineHaveZeroHeight(nsIFrame* aFrame)
|
||||||
|
{
|
||||||
|
nsLineBox* line = FindContainingLine(aFrame);
|
||||||
|
if (!line)
|
||||||
|
return aFrame->GetRect().height == 0;
|
||||||
|
PRInt32 count = line->GetChildCount();
|
||||||
|
for (nsIFrame* f = line->mFirstChild; count > 0; --count, f = f->GetNextSibling())
|
||||||
|
{
|
||||||
|
if (f->GetRect().height != 0)
|
||||||
|
return PR_FALSE;
|
||||||
|
}
|
||||||
|
return PR_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
nsCaret::nsCaret()
|
nsCaret::nsCaret()
|
||||||
|
@ -277,7 +357,8 @@ void nsCaret::SetCaretReadOnly(PRBool inMakeReadonly)
|
||||||
mReadOnly = inMakeReadonly;
|
mReadOnly = inMakeReadonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsIFrame* nsCaret::GetGeometry(nsISelection* aSelection, nsRect* aRect)
|
nsIFrame* nsCaret::GetGeometry(nsISelection* aSelection, nsRect* aRect,
|
||||||
|
nscoord* aBidiIndicatorSize)
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIDOMNode> focusNode;
|
nsCOMPtr<nsIDOMNode> focusNode;
|
||||||
nsresult rv = aSelection->GetFocusNode(getter_AddRefs(focusNode));
|
nsresult rv = aSelection->GetFocusNode(getter_AddRefs(focusNode));
|
||||||
|
@ -312,10 +393,47 @@ nsIFrame* nsCaret::GetGeometry(nsISelection* aSelection, nsRect* aRect)
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return nsnull;
|
return nsnull;
|
||||||
|
|
||||||
// now add the frame offset to the view offset, and we're done
|
|
||||||
nscoord height = theFrame->GetContentRect().height;
|
nscoord height = theFrame->GetContentRect().height;
|
||||||
nscoord width = ComputeMetrics(theFrame, theFrameOffset, height).mCaretWidth;
|
if (height == 0) {
|
||||||
*aRect = nsRect(framePos.x, 0, width, height);
|
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;
|
return theFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,71 +723,6 @@ nsCaret::DrawAtPositionWithHint(nsIDOMNode* aNode,
|
||||||
return PR_TRUE;
|
return PR_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the first frame in an in-order traversal of the frame subtree rooted
|
|
||||||
* at aFrame which is either a text frame logically at the end of a line,
|
|
||||||
* or which is aStopAtFrame. Return null if no such frame is found. We don't
|
|
||||||
* descend into the children of non-eLineParticipant frames.
|
|
||||||
*/
|
|
||||||
static nsIFrame*
|
|
||||||
CheckForTrailingTextFrameRecursive(nsIFrame* aFrame, nsIFrame* aStopAtFrame)
|
|
||||||
{
|
|
||||||
if (aFrame == aStopAtFrame ||
|
|
||||||
((aFrame->GetType() == nsGkAtoms::textFrame &&
|
|
||||||
(static_cast<nsTextFrame*>(aFrame))->IsAtEndOfLine())))
|
|
||||||
return aFrame;
|
|
||||||
if (!aFrame->IsFrameOfType(nsIFrame::eLineParticipant))
|
|
||||||
return nsnull;
|
|
||||||
|
|
||||||
for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling())
|
|
||||||
{
|
|
||||||
nsIFrame* r = CheckForTrailingTextFrameRecursive(f, aStopAtFrame);
|
|
||||||
if (r)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
return nsnull;
|
|
||||||
}
|
|
||||||
|
|
||||||
static nsLineBox*
|
|
||||||
FindContainingLine(nsIFrame* aFrame)
|
|
||||||
{
|
|
||||||
while (aFrame && aFrame->IsFrameOfType(nsIFrame::eLineParticipant))
|
|
||||||
{
|
|
||||||
nsIFrame* parent = aFrame->GetParent();
|
|
||||||
nsBlockFrame* blockParent = nsLayoutUtils::GetAsBlock(parent);
|
|
||||||
if (blockParent)
|
|
||||||
{
|
|
||||||
PRBool isValid;
|
|
||||||
nsBlockInFlowLineIterator iter(blockParent, aFrame, &isValid);
|
|
||||||
return isValid ? iter.GetLine().get() : nsnull;
|
|
||||||
}
|
|
||||||
aFrame = parent;
|
|
||||||
}
|
|
||||||
return nsnull;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
AdjustCaretFrameForLineEnd(nsIFrame** aFrame, PRInt32* aOffset)
|
|
||||||
{
|
|
||||||
nsLineBox* line = FindContainingLine(*aFrame);
|
|
||||||
if (!line)
|
|
||||||
return;
|
|
||||||
PRInt32 count = line->GetChildCount();
|
|
||||||
for (nsIFrame* f = line->mFirstChild; count > 0; --count, f = f->GetNextSibling())
|
|
||||||
{
|
|
||||||
nsIFrame* r = CheckForTrailingTextFrameRecursive(f, *aFrame);
|
|
||||||
if (r == *aFrame)
|
|
||||||
return;
|
|
||||||
if (r)
|
|
||||||
{
|
|
||||||
*aFrame = r;
|
|
||||||
NS_ASSERTION(r->GetType() == nsGkAtoms::textFrame, "Expected text frame");
|
|
||||||
*aOffset = (static_cast<nsTextFrame*>(r))->GetContentEnd();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsCaret::GetCaretFrameForNodeOffset(nsIContent* aContentNode,
|
nsCaret::GetCaretFrameForNodeOffset(nsIContent* aContentNode,
|
||||||
PRInt32 aOffset,
|
PRInt32 aOffset,
|
||||||
|
@ -1020,103 +1073,24 @@ void nsCaret::DrawCaret(PRBool aInvalidate)
|
||||||
ToggleDrawnStatus();
|
ToggleDrawnStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
static PRBool
|
|
||||||
FramesOnSameLineHaveZeroHeight(nsIFrame* aFrame)
|
|
||||||
{
|
|
||||||
nsLineBox* line = FindContainingLine(aFrame);
|
|
||||||
if (!line)
|
|
||||||
return aFrame->GetRect().height == 0;
|
|
||||||
PRInt32 count = line->GetChildCount();
|
|
||||||
for (nsIFrame* f = line->mFirstChild; count > 0; --count, f = f->GetNextSibling())
|
|
||||||
{
|
|
||||||
if (f->GetRect().height != 0)
|
|
||||||
return PR_FALSE;
|
|
||||||
}
|
|
||||||
return PR_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult nsCaret::UpdateCaretRects(nsIFrame* aFrame, PRInt32 aFrameOffset)
|
nsresult nsCaret::UpdateCaretRects(nsIFrame* aFrame, PRInt32 aFrameOffset)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(aFrame, "Should have a frame here");
|
NS_ASSERTION(aFrame, "Should have a frame here");
|
||||||
|
|
||||||
nsRect frameRect = aFrame->GetContentRect();
|
|
||||||
frameRect.x = 0;
|
|
||||||
frameRect.y = 0;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
|
|
||||||
if (!presShell) return NS_ERROR_FAILURE;
|
|
||||||
|
|
||||||
// If we got a zero-height frame we should figure out a height. We have to do
|
|
||||||
// this after we've got an RC.
|
|
||||||
if (frameRect.height == 0)
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIFontMetrics> fm;
|
|
||||||
nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm));
|
|
||||||
|
|
||||||
if (fm)
|
|
||||||
{
|
|
||||||
nscoord ascent, descent;
|
|
||||||
fm->GetMaxAscent(ascent);
|
|
||||||
fm->GetMaxDescent(descent);
|
|
||||||
frameRect.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))
|
|
||||||
frameRect.y -= ascent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mCaretRect = frameRect;
|
|
||||||
nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
|
nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
|
||||||
nsCOMPtr<nsISelectionPrivate> privateSelection = do_QueryInterface(domSelection);
|
nscoord bidiIndicatorSize;
|
||||||
|
GetGeometry(domSelection, &mCaretRect, &bidiIndicatorSize);
|
||||||
nsPoint framePos;
|
|
||||||
|
|
||||||
// if cache in selection is available, apply it, else refresh it
|
|
||||||
nsresult rv = privateSelection->GetCachedFrameOffset(aFrame, aFrameOffset,
|
|
||||||
framePos);
|
|
||||||
if (NS_FAILED(rv))
|
|
||||||
{
|
|
||||||
mCaretRect.Empty();
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
mCaretRect += framePos;
|
|
||||||
Metrics metrics = ComputeMetrics(aFrame, aFrameOffset, mCaretRect.height);
|
|
||||||
mCaretRect.width = metrics.mCaretWidth;
|
|
||||||
|
|
||||||
// Clamp our 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 scrolled frame that we're in.
|
|
||||||
nsIScrollableFrame *sf = do_QueryFrame(scrollFrame);
|
|
||||||
nsIFrame *scrolled = sf->GetScrolledFrame();
|
|
||||||
nsRect caretInScroll = mCaretRect + aFrame->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)
|
|
||||||
mCaretRect.x -= overflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
// on RTL frames the right edge of mCaretRect must be equal to framePos
|
// on RTL frames the right edge of mCaretRect must be equal to framePos
|
||||||
const nsStyleVisibility* vis = aFrame->GetStyleVisibility();
|
const nsStyleVisibility* vis = aFrame->GetStyleVisibility();
|
||||||
if (NS_STYLE_DIRECTION_RTL == vis->mDirection)
|
if (NS_STYLE_DIRECTION_RTL == vis->mDirection)
|
||||||
mCaretRect.x -= mCaretRect.width;
|
mCaretRect.x -= mCaretRect.width;
|
||||||
|
|
||||||
return UpdateHookRect(presShell->GetPresContext(), metrics);
|
return UpdateHookRect(domSelection, bidiIndicatorSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult nsCaret::UpdateHookRect(nsPresContext* aPresContext,
|
nsresult nsCaret::UpdateHookRect(nsISelection* aSelection,
|
||||||
const Metrics& aMetrics)
|
nscoord aBidiIndicatorSize)
|
||||||
{
|
{
|
||||||
mHookRect.Empty();
|
mHookRect.Empty();
|
||||||
|
|
||||||
|
@ -1142,24 +1116,19 @@ nsresult nsCaret::UpdateHookRect(nsPresContext* aPresContext,
|
||||||
* without drawing the caret in the old position.
|
* without drawing the caret in the old position.
|
||||||
*/
|
*/
|
||||||
mKeyboardRTL = isCaretRTL;
|
mKeyboardRTL = isCaretRTL;
|
||||||
nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
|
if (NS_SUCCEEDED(aSelection->SelectionLanguageChange(mKeyboardRTL)))
|
||||||
if (domSelection)
|
|
||||||
{
|
|
||||||
if (NS_SUCCEEDED(domSelection->SelectionLanguageChange(mKeyboardRTL)))
|
|
||||||
{
|
{
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// If keyboard language is RTL, draw the hook on the left; if LTR, to the right
|
// If keyboard language is RTL, draw the hook on the left; if LTR, to the right
|
||||||
// The height of the hook rectangle is the same as the width of the caret
|
// The height of the hook rectangle is the same as the width of the caret
|
||||||
// rectangle.
|
// rectangle.
|
||||||
nscoord bidiIndicatorSize = aMetrics.mBidiIndicatorSize;
|
|
||||||
mHookRect.SetRect(mCaretRect.x + ((isCaretRTL) ?
|
mHookRect.SetRect(mCaretRect.x + ((isCaretRTL) ?
|
||||||
bidiIndicatorSize * -1 :
|
aBidiIndicatorSize * -1 :
|
||||||
mCaretRect.width),
|
mCaretRect.width),
|
||||||
mCaretRect.y + bidiIndicatorSize,
|
mCaretRect.y + aBidiIndicatorSize,
|
||||||
bidiIndicatorSize,
|
aBidiIndicatorSize,
|
||||||
mCaretRect.width);
|
mCaretRect.width);
|
||||||
}
|
}
|
||||||
#endif //IBMBIDI
|
#endif //IBMBIDI
|
||||||
|
|
|
@ -108,9 +108,12 @@ class nsCaret : public nsISelectionListener
|
||||||
* the focus node/offset of aSelection (assuming it would be drawn,
|
* the focus node/offset of aSelection (assuming it would be drawn,
|
||||||
* i.e., disregarding blink status). The geometry is stored in aRect,
|
* i.e., disregarding blink status). The geometry is stored in aRect,
|
||||||
* and we return the frame aRect is relative to.
|
* and we return the frame aRect is relative to.
|
||||||
|
* @param aRect must be non-null
|
||||||
|
* @param aBidiIndicatorSize if non-null, set to the bidi indicator size.
|
||||||
*/
|
*/
|
||||||
virtual nsIFrame* GetGeometry(nsISelection* aSelection,
|
virtual nsIFrame* GetGeometry(nsISelection* aSelection,
|
||||||
nsRect* aRect);
|
nsRect* aRect,
|
||||||
|
nscoord* aBidiIndicatorSize = nsnull);
|
||||||
|
|
||||||
/** EraseCaret
|
/** EraseCaret
|
||||||
* this will erase the caret if its drawn and reset drawn status
|
* this will erase the caret if its drawn and reset drawn status
|
||||||
|
@ -223,8 +226,8 @@ protected:
|
||||||
void DrawCaret(PRBool aInvalidate);
|
void DrawCaret(PRBool aInvalidate);
|
||||||
void DrawCaretAfterBriefDelay();
|
void DrawCaretAfterBriefDelay();
|
||||||
nsresult UpdateCaretRects(nsIFrame* aFrame, PRInt32 aFrameOffset);
|
nsresult UpdateCaretRects(nsIFrame* aFrame, PRInt32 aFrameOffset);
|
||||||
nsresult UpdateHookRect(nsPresContext* aPresContext,
|
nsresult UpdateHookRect(nsISelection* aSelection,
|
||||||
const Metrics& aMetrics);
|
nscoord aBidiIndicatorSize);
|
||||||
static void InvalidateRects(const nsRect &aRect, const nsRect &aHook,
|
static void InvalidateRects(const nsRect &aRect, const nsRect &aHook,
|
||||||
nsIFrame *aFrame);
|
nsIFrame *aFrame);
|
||||||
nsRect GetHookRect()
|
nsRect GetHookRect()
|
||||||
|
|
Загрузка…
Ссылка в новой задаче