Fix 51747 -- caret not drawing at the end of a line. Fix offset of clip. r=kin

This commit is contained in:
sfraser%netscape.com 2000-09-07 20:21:22 +00:00
Родитель b6249a8f38
Коммит 323042d90a
4 изменённых файлов: 288 добавлений и 280 удалений

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

@ -49,6 +49,11 @@
#include "nsCaret.h" #include "nsCaret.h"
// Because of drawing issues, we currently always make a new RC. See bug 28068
// Before removing this, stuff will need to be fixed and tested on all platforms.
// For example, turning this off on Mac right now causes drawing problems on pages
// with form elements.
#define DONT_REUSE_RENDERING_CONTEXT
static NS_DEFINE_IID(kLookAndFeelCID, NS_LOOKANDFEEL_CID); static NS_DEFINE_IID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
@ -63,6 +68,7 @@ nsCaret::nsCaret()
, mReadOnly(PR_TRUE) , mReadOnly(PR_TRUE)
, mDrawn(PR_FALSE) , mDrawn(PR_FALSE)
, mLastCaretFrame(nsnull) , mLastCaretFrame(nsnull)
, mLastCaretView(nsnull)
, mLastContentOffset(0) , mLastContentOffset(0)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
@ -326,6 +332,7 @@ NS_IMETHODIMP nsCaret::ClearFrameRefs(nsIFrame* aFrame)
if (mLastCaretFrame == aFrame) if (mLastCaretFrame == aFrame)
{ {
mLastCaretFrame = nsnull; // frames are not refcounted. mLastCaretFrame = nsnull; // frames are not refcounted.
mLastCaretView = nsnull;
mLastContentOffset = 0; mLastContentOffset = 0;
} }
@ -432,63 +439,76 @@ PRBool nsCaret::SetupDrawingFrameAndOffset()
{ {
if (!mDomSelectionWeak) if (!mDomSelectionWeak)
return PR_FALSE; return PR_FALSE;
nsCOMPtr<nsIDOMSelection> domSelection = do_QueryReferent(mDomSelectionWeak);
PRBool isCollapsed; nsCOMPtr<nsIDOMSelection> domSelection = do_QueryReferent(mDomSelectionWeak);
if (!domSelection) return PR_FALSE;
PRBool isCollapsed = PR_FALSE;
domSelection->GetIsCollapsed(&isCollapsed);
if (!isCollapsed) return PR_FALSE;
if (domSelection && NS_SUCCEEDED(domSelection->GetIsCollapsed(&isCollapsed)) && isCollapsed) // start and end parent should be the same since we are collapsed
{ nsCOMPtr<nsIDOMNode> focusNode;
// start and end parent should be the same since we are collapsed domSelection->GetFocusNode(getter_AddRefs(focusNode));
nsCOMPtr<nsIDOMNode> focusNode; if (!focusNode) return PR_FALSE;
PRInt32 contentOffset;
PRInt32 contentOffset;
if (NS_SUCCEEDED(domSelection->GetFocusNode(getter_AddRefs(focusNode))) && focusNode && if (NS_FAILED(domSelection->GetFocusOffset(&contentOffset)))
NS_SUCCEEDED(domSelection->GetFocusOffset(&contentOffset))) return PR_FALSE;
{
nsCOMPtr<nsIContent>contentNode = do_QueryInterface(focusNode); nsCOMPtr<nsIContent> contentNode = do_QueryInterface(focusNode);
if (!contentNode) return PR_FALSE;
if (contentNode) //get frame selection and find out what frame to use...
{ nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
nsIFrame* theFrame = nsnull; if (!presShell)
PRInt32 theFrameOffset = 0; return PR_FALSE;
nsresult err;
//get frame selection and find out what frame to use... nsCOMPtr<nsIFrameSelection> frameSelection;
nsCOMPtr<nsIFrameSelection> frameSelection; presShell->GetFrameSelection(getter_AddRefs(frameSelection));
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell); if (!frameSelection)
if (!presShell) return PR_FALSE;
return PR_FALSE;
err = presShell->GetFrameSelection(getter_AddRefs(frameSelection));
if (NS_FAILED(err) || !frameSelection)
return PR_FALSE;
PRBool hintRight; PRBool hintRight;
domSelection->GetHint(&hintRight);//translate hint. domSelection->GetHint(&hintRight);//translate hint.
nsIFrameSelection::HINT hint; nsIFrameSelection::HINT hint;
if (hintRight) hint = (hintRight) ? nsIFrameSelection::HINTRIGHT : nsIFrameSelection::HINTLEFT;
hint = nsIFrameSelection::HINTRIGHT;
else
hint = nsIFrameSelection::HINTLEFT;
err = frameSelection->GetFrameForNodeOffset(contentNode, contentOffset, hint, &theFrame, &theFrameOffset); nsIFrame* theFrame = nsnull;
if (NS_FAILED(err)) PRInt32 theFrameOffset = 0;
return PR_FALSE;
// mark the frame, so we get notified on deletion. nsresult rv = frameSelection->GetFrameForNodeOffset(contentNode, contentOffset, hint, &theFrame, &theFrameOffset);
// frames are never unmarked, which means that we'll touch every frame we visit. if (NS_FAILED(rv) || !theFrame)
// this is not ideal. return PR_FALSE;
nsFrameState frameState;
theFrame->GetFrameState(&frameState); // now we have a frame, check whether it's appropriate to show the caret here
frameState |= NS_FRAME_EXTERNAL_REFERENCE; const nsStyleUserInterface* userinterface;
theFrame->SetFrameState(frameState); theFrame->GetStyleData(eStyleStruct_UserInterface, (const nsStyleStruct*&)userinterface);
if (userinterface)
mLastCaretFrame = theFrame; {
mLastContentOffset = theFrameOffset; if (
return PR_TRUE; #ifdef SUPPORT_USER_MODIFY
} // editable content still defaults to NS_STYLE_USER_MODIFY_READ_ONLY at present. See bug 15284
} (userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) ||
#endif
(userinterface->mUserInput == NS_STYLE_USER_INPUT_NONE) ||
(userinterface->mUserInput == NS_STYLE_USER_INPUT_DISABLED))
{
return PR_FALSE;
}
} }
return PR_FALSE; // mark the frame, so we get notified on deletion.
// frames are never unmarked, which means that we'll touch every frame we visit.
// this is not ideal.
nsFrameState frameState;
theFrame->GetFrameState(&frameState);
frameState |= NS_FRAME_EXTERNAL_REFERENCE;
theFrame->SetFrameState(frameState);
mLastCaretFrame = theFrame;
mLastContentOffset = theFrameOffset;
return PR_TRUE;
} }
@ -510,9 +530,11 @@ void nsCaret::GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordTy
viewOffset.x = 0; viewOffset.x = 0;
viewOffset.y = 0; viewOffset.y = 0;
nsPoint withinViewOffset(0, 0);
// get the offset of this frame from its parent view (walks up frame hierarchy) // get the offset of this frame from its parent view (walks up frame hierarchy)
nsIView* theView = nsnull; nsIView* theView = nsnull;
caretFrame->GetOffsetFromView(presContext, viewOffset, &theView); caretFrame->GetOffsetFromView(presContext, withinViewOffset, &theView);
if (theView == nsnull) return; if (theView == nsnull) return;
nsIView* returnView = nsnull; // views are not refcounted nsIView* returnView = nsnull; // views are not refcounted
@ -525,6 +547,8 @@ void nsCaret::GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordTy
nsIView* startingView = theView; nsIView* startingView = theView;
nsIScrollableView* scrollableView = nsnull; nsIScrollableView* scrollableView = nsnull;
nsPoint drawViewOffset(0, 0); // offset to the view we are using to draw
// walk up to the first view with a widget // walk up to the first view with a widget
do { do {
theView->GetPosition(&x, &y); theView->GetPosition(&x, &y);
@ -535,19 +559,20 @@ void nsCaret::GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordTy
PRBool hasWidget; PRBool hasWidget;
theView->HasWidget(&hasWidget); theView->HasWidget(&hasWidget);
if (hasWidget) if (hasWidget)
{ {
returnView = theView; returnView = theView;
break; break;
} }
viewOffset.x += x; drawViewOffset.x += x;
viewOffset.y += y; drawViewOffset.y += y;
theView->GetParent(theView); theView->GetParent(theView);
} while (theView); } while (theView);
viewOffset = withinViewOffset;
viewOffset += drawViewOffset;
if (scrollableView) if (scrollableView)
{ {
const nsIView* clipView = nsnull; const nsIView* clipView = nsnull;
@ -558,7 +583,7 @@ void nsCaret::GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordTy
clipView->GetBounds(bounds); clipView->GetBounds(bounds);
scrollableView->GetScrollPosition(bounds.x, bounds.y); scrollableView->GetScrollPosition(bounds.x, bounds.y);
bounds += viewOffset; // offset to coords of returned view bounds += drawViewOffset; // offset to coords of returned view
outClipRect = bounds; outClipRect = bounds;
} }
else else
@ -626,14 +651,22 @@ PRBool nsCaret::MustDrawCaret()
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
DrawCaretWithContext DrawCaret
By this point, the caret rect should have been set up.
----------------------------------------------------------------------------- */ ----------------------------------------------------------------------------- */
void nsCaret::DrawCaretWithContext(nsIRenderingContext* inRendContext) void nsCaret::DrawCaret()
{ {
// do we need to draw the caret at all?
if (!MustDrawCaret())
return;
// if we are drawing, not erasing, then set up the frame etc.
if (!mDrawn)
{
if (!SetupDrawingFrameAndOffset())
return;
}
NS_ASSERTION(mLastCaretFrame != nsnull, "Should have a frame here"); NS_ASSERTION(mLastCaretFrame != nsnull, "Should have a frame here");
@ -658,37 +691,44 @@ void nsCaret::DrawCaretWithContext(nsIRenderingContext* inRendContext)
frameRect += viewOffset; frameRect += viewOffset;
nsCOMPtr<nsIPresContext> presContext;
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell); nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
if (presShell) if (!presShell) return;
{
if (NS_FAILED(presShell->GetPresContext(getter_AddRefs(presContext)))) nsCOMPtr<nsIPresContext> presContext;
return; if (NS_FAILED(presShell->GetPresContext(getter_AddRefs(presContext))))
}
else
return; return;
// make a rendering context, if we didn't get passed one // if the view changed, or we don't have a rendering context, make one
nsCOMPtr<nsIRenderingContext> localRC = do_QueryInterface(inRendContext); // OK if inRendContext is null // because of drawing issues, always make a new RC at the momemt. See bug 28068
if (!localRC) if (
#ifdef DONT_REUSE_RENDERING_CONTEXT
PR_TRUE ||
#endif
(mLastCaretView != drawingView) || !mRendContext)
{ {
mRendContext = nsnull; // free existing one if we have one
nsCOMPtr<nsIDeviceContext> dx; nsCOMPtr<nsIDeviceContext> dx;
if (NS_FAILED(presContext->GetDeviceContext(getter_AddRefs(dx))) || !dx) if (NS_FAILED(presContext->GetDeviceContext(getter_AddRefs(dx))) || !dx)
return; return;
if (NS_FAILED(dx->CreateRenderingContext(drawingView, *getter_AddRefs(localRC))) || !localRC) if (NS_FAILED(dx->CreateRenderingContext(drawingView, *getter_AddRefs(mRendContext))) || !mRendContext)
return; return;
} }
// push a known good state
mRendContext->PushState();
// views are not refcounted
mLastCaretView = drawingView;
if (!mDrawn) if (!mDrawn)
{ {
nsPoint framePos(0, 0); nsPoint framePos(0, 0);
nsRect caretRect = frameRect; nsRect caretRect = frameRect;
mLastCaretFrame->GetPointFromOffset(presContext, localRC, mLastContentOffset, &framePos); mLastCaretFrame->GetPointFromOffset(presContext, mRendContext, mLastContentOffset, &framePos);
caretRect += framePos; caretRect += framePos;
//printf("Content offset %ld, frame offset %ld\n", focusOffset, framePos.x); //printf("Content offset %ld, frame offset %ld\n", focusOffset, framePos.x);
if(mCaretTwipsWidth < 0) if(mCaretTwipsWidth < 0)
@ -721,52 +761,17 @@ void nsCaret::DrawCaretWithContext(nsIRenderingContext* inRendContext)
inRendContext.SetColor(NS_RGB(85, 85, 85)); // we are drawing it; gray inRendContext.SetColor(NS_RGB(85, 85, 85)); // we are drawing it; gray
*/ */
localRC->SetColor(NS_RGB(255,255,255)); mRendContext->SetColor(NS_RGB(255,255,255));
localRC->InvertRect(mCaretRect); mRendContext->InvertRect(mCaretRect);
PRBool emptyClip; // I know what you're thinking. "Did he fire six shots or only five?"
mRendContext->PopState(emptyClip);
ToggleDrawnStatus(); ToggleDrawnStatus();
} #ifdef DONT_REUSE_RENDERING_CONTEXT
mRendContext = nsnull;
//----------------------------------------------------------------------------- #endif
void nsCaret::DrawCaret()
{
// do we need to draw the caret at all?
if (!MustDrawCaret())
return;
// if we are drawing, not erasing, then set up the frame etc.
if (!mDrawn)
{
if (! SetupDrawingFrameAndOffset())
return;
}
DrawCaretWithContext(nsnull);
}
//-----------------------------------------------------------------------------
void nsCaret::RefreshDrawCaret(nsIView *aView, nsIRenderingContext& inRendContext, const nsRect& aDirtyRect)
{
/*
if (! SetupDrawingFrameAndOffset())
return;
NS_ASSERTION(mLastCaretFrame != nsnull, "Should have a frame here");
nsPoint viewOffset(0, 0);
nsIView *drawingView;
//GetViewForRendering(viewOffset, drawingView);
mLastCaretFrame->GetOffsetFromView(viewOffset, &drawingView);
// are we in the view that is being painted?
if (drawingView == nsnull || drawingView != aView)
return;
mDrawn = PR_FALSE; // we're rendering to a view that is being redrawn
DrawCaretWithContext(inRendContext);
*/
} }
#ifdef XP_MAC #ifdef XP_MAC

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

@ -24,12 +24,13 @@
#include "nsCoord.h" #include "nsCoord.h"
#include "nsIDOMSelectionListener.h" #include "nsIDOMSelectionListener.h"
#include "nsIRenderingContext.h"
#include "nsITimer.h"
#include "nsICaret.h" #include "nsICaret.h"
#include "nsWeakPtr.h" #include "nsWeakPtr.h"
class nsITimer;
class nsIView; class nsIView;
class nsIRenderingContext;
class nsISelectionController; class nsISelectionController;
// {E14B66F6-BFC5-11d2-B57E-00105AA83B2F} // {E14B66F6-BFC5-11d2-B57E-00105AA83B2F}
@ -83,30 +84,28 @@ class nsCaret : public nsICaret,
void GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordType, nsPoint &viewOffset, nsRect& outClipRect, nsIView* &outView); void GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordType, nsPoint &viewOffset, nsRect& outClipRect, nsIView* &outView);
PRBool SetupDrawingFrameAndOffset(); PRBool SetupDrawingFrameAndOffset();
PRBool MustDrawCaret(); PRBool MustDrawCaret();
void RefreshDrawCaret(nsIView *aView, nsIRenderingContext& inRendContext, const nsRect& aDirtyRect);
void DrawCaretWithContext(nsIRenderingContext* inRendContext);
void DrawCaret(); void DrawCaret();
void ToggleDrawnStatus() { mDrawn = !mDrawn; } void ToggleDrawnStatus() { mDrawn = !mDrawn; }
nsCOMPtr<nsIWeakReference> mPresShell; protected:
nsCOMPtr<nsITimer> mBlinkTimer; nsWeakPtr mPresShell;
nsWeakPtr mDomSelectionWeak;
PRUint32 mBlinkRate; // time for one cyle (off then on), in milliseconds nsCOMPtr<nsITimer> mBlinkTimer;
nscoord mCaretTwipsWidth; // caret width in twips nsCOMPtr<nsIRenderingContext> mRendContext;
nscoord mCaretPixelsWidth; // caret width in pixels
PRUint32 mBlinkRate; // time for one cyle (off then on), in milliseconds
nscoord mCaretTwipsWidth; // caret width in twips
nscoord mCaretPixelsWidth; // caret width in pixels
PRBool mVisible; // is the caret blinking PRPackedBool mVisible; // is the caret blinking
PRBool mReadOnly; // it the caret in readonly state (draws differently) PRPackedBool mDrawn; // this should be mutable
PRPackedBool mReadOnly; // it the caret in readonly state (draws differently)
private:
PRBool mDrawn; // this should be mutable
nsRect mCaretRect; // the last caret rect nsRect mCaretRect; // the last caret rect
nsIFrame* mLastCaretFrame; // store the frame the caret was last drawn in. nsIFrame* mLastCaretFrame; // store the frame the caret was last drawn in.
nsIView* mLastCaretView; // last view that we used for drawing. Cached so we can tell when we need to make a new RC
PRInt32 mLastContentOffset; PRInt32 mLastContentOffset;
nsWeakPtr mDomSelectionWeak;
}; };

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

@ -49,6 +49,11 @@
#include "nsCaret.h" #include "nsCaret.h"
// Because of drawing issues, we currently always make a new RC. See bug 28068
// Before removing this, stuff will need to be fixed and tested on all platforms.
// For example, turning this off on Mac right now causes drawing problems on pages
// with form elements.
#define DONT_REUSE_RENDERING_CONTEXT
static NS_DEFINE_IID(kLookAndFeelCID, NS_LOOKANDFEEL_CID); static NS_DEFINE_IID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
@ -63,6 +68,7 @@ nsCaret::nsCaret()
, mReadOnly(PR_TRUE) , mReadOnly(PR_TRUE)
, mDrawn(PR_FALSE) , mDrawn(PR_FALSE)
, mLastCaretFrame(nsnull) , mLastCaretFrame(nsnull)
, mLastCaretView(nsnull)
, mLastContentOffset(0) , mLastContentOffset(0)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
@ -326,6 +332,7 @@ NS_IMETHODIMP nsCaret::ClearFrameRefs(nsIFrame* aFrame)
if (mLastCaretFrame == aFrame) if (mLastCaretFrame == aFrame)
{ {
mLastCaretFrame = nsnull; // frames are not refcounted. mLastCaretFrame = nsnull; // frames are not refcounted.
mLastCaretView = nsnull;
mLastContentOffset = 0; mLastContentOffset = 0;
} }
@ -432,63 +439,76 @@ PRBool nsCaret::SetupDrawingFrameAndOffset()
{ {
if (!mDomSelectionWeak) if (!mDomSelectionWeak)
return PR_FALSE; return PR_FALSE;
nsCOMPtr<nsIDOMSelection> domSelection = do_QueryReferent(mDomSelectionWeak);
PRBool isCollapsed; nsCOMPtr<nsIDOMSelection> domSelection = do_QueryReferent(mDomSelectionWeak);
if (!domSelection) return PR_FALSE;
PRBool isCollapsed = PR_FALSE;
domSelection->GetIsCollapsed(&isCollapsed);
if (!isCollapsed) return PR_FALSE;
if (domSelection && NS_SUCCEEDED(domSelection->GetIsCollapsed(&isCollapsed)) && isCollapsed) // start and end parent should be the same since we are collapsed
{ nsCOMPtr<nsIDOMNode> focusNode;
// start and end parent should be the same since we are collapsed domSelection->GetFocusNode(getter_AddRefs(focusNode));
nsCOMPtr<nsIDOMNode> focusNode; if (!focusNode) return PR_FALSE;
PRInt32 contentOffset;
PRInt32 contentOffset;
if (NS_SUCCEEDED(domSelection->GetFocusNode(getter_AddRefs(focusNode))) && focusNode && if (NS_FAILED(domSelection->GetFocusOffset(&contentOffset)))
NS_SUCCEEDED(domSelection->GetFocusOffset(&contentOffset))) return PR_FALSE;
{
nsCOMPtr<nsIContent>contentNode = do_QueryInterface(focusNode); nsCOMPtr<nsIContent> contentNode = do_QueryInterface(focusNode);
if (!contentNode) return PR_FALSE;
if (contentNode) //get frame selection and find out what frame to use...
{ nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
nsIFrame* theFrame = nsnull; if (!presShell)
PRInt32 theFrameOffset = 0; return PR_FALSE;
nsresult err;
//get frame selection and find out what frame to use... nsCOMPtr<nsIFrameSelection> frameSelection;
nsCOMPtr<nsIFrameSelection> frameSelection; presShell->GetFrameSelection(getter_AddRefs(frameSelection));
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell); if (!frameSelection)
if (!presShell) return PR_FALSE;
return PR_FALSE;
err = presShell->GetFrameSelection(getter_AddRefs(frameSelection));
if (NS_FAILED(err) || !frameSelection)
return PR_FALSE;
PRBool hintRight; PRBool hintRight;
domSelection->GetHint(&hintRight);//translate hint. domSelection->GetHint(&hintRight);//translate hint.
nsIFrameSelection::HINT hint; nsIFrameSelection::HINT hint;
if (hintRight) hint = (hintRight) ? nsIFrameSelection::HINTRIGHT : nsIFrameSelection::HINTLEFT;
hint = nsIFrameSelection::HINTRIGHT;
else
hint = nsIFrameSelection::HINTLEFT;
err = frameSelection->GetFrameForNodeOffset(contentNode, contentOffset, hint, &theFrame, &theFrameOffset); nsIFrame* theFrame = nsnull;
if (NS_FAILED(err)) PRInt32 theFrameOffset = 0;
return PR_FALSE;
// mark the frame, so we get notified on deletion. nsresult rv = frameSelection->GetFrameForNodeOffset(contentNode, contentOffset, hint, &theFrame, &theFrameOffset);
// frames are never unmarked, which means that we'll touch every frame we visit. if (NS_FAILED(rv) || !theFrame)
// this is not ideal. return PR_FALSE;
nsFrameState frameState;
theFrame->GetFrameState(&frameState); // now we have a frame, check whether it's appropriate to show the caret here
frameState |= NS_FRAME_EXTERNAL_REFERENCE; const nsStyleUserInterface* userinterface;
theFrame->SetFrameState(frameState); theFrame->GetStyleData(eStyleStruct_UserInterface, (const nsStyleStruct*&)userinterface);
if (userinterface)
mLastCaretFrame = theFrame; {
mLastContentOffset = theFrameOffset; if (
return PR_TRUE; #ifdef SUPPORT_USER_MODIFY
} // editable content still defaults to NS_STYLE_USER_MODIFY_READ_ONLY at present. See bug 15284
} (userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) ||
#endif
(userinterface->mUserInput == NS_STYLE_USER_INPUT_NONE) ||
(userinterface->mUserInput == NS_STYLE_USER_INPUT_DISABLED))
{
return PR_FALSE;
}
} }
return PR_FALSE; // mark the frame, so we get notified on deletion.
// frames are never unmarked, which means that we'll touch every frame we visit.
// this is not ideal.
nsFrameState frameState;
theFrame->GetFrameState(&frameState);
frameState |= NS_FRAME_EXTERNAL_REFERENCE;
theFrame->SetFrameState(frameState);
mLastCaretFrame = theFrame;
mLastContentOffset = theFrameOffset;
return PR_TRUE;
} }
@ -510,9 +530,11 @@ void nsCaret::GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordTy
viewOffset.x = 0; viewOffset.x = 0;
viewOffset.y = 0; viewOffset.y = 0;
nsPoint withinViewOffset(0, 0);
// get the offset of this frame from its parent view (walks up frame hierarchy) // get the offset of this frame from its parent view (walks up frame hierarchy)
nsIView* theView = nsnull; nsIView* theView = nsnull;
caretFrame->GetOffsetFromView(presContext, viewOffset, &theView); caretFrame->GetOffsetFromView(presContext, withinViewOffset, &theView);
if (theView == nsnull) return; if (theView == nsnull) return;
nsIView* returnView = nsnull; // views are not refcounted nsIView* returnView = nsnull; // views are not refcounted
@ -525,6 +547,8 @@ void nsCaret::GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordTy
nsIView* startingView = theView; nsIView* startingView = theView;
nsIScrollableView* scrollableView = nsnull; nsIScrollableView* scrollableView = nsnull;
nsPoint drawViewOffset(0, 0); // offset to the view we are using to draw
// walk up to the first view with a widget // walk up to the first view with a widget
do { do {
theView->GetPosition(&x, &y); theView->GetPosition(&x, &y);
@ -535,19 +559,20 @@ void nsCaret::GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordTy
PRBool hasWidget; PRBool hasWidget;
theView->HasWidget(&hasWidget); theView->HasWidget(&hasWidget);
if (hasWidget) if (hasWidget)
{ {
returnView = theView; returnView = theView;
break; break;
} }
viewOffset.x += x; drawViewOffset.x += x;
viewOffset.y += y; drawViewOffset.y += y;
theView->GetParent(theView); theView->GetParent(theView);
} while (theView); } while (theView);
viewOffset = withinViewOffset;
viewOffset += drawViewOffset;
if (scrollableView) if (scrollableView)
{ {
const nsIView* clipView = nsnull; const nsIView* clipView = nsnull;
@ -558,7 +583,7 @@ void nsCaret::GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordTy
clipView->GetBounds(bounds); clipView->GetBounds(bounds);
scrollableView->GetScrollPosition(bounds.x, bounds.y); scrollableView->GetScrollPosition(bounds.x, bounds.y);
bounds += viewOffset; // offset to coords of returned view bounds += drawViewOffset; // offset to coords of returned view
outClipRect = bounds; outClipRect = bounds;
} }
else else
@ -626,14 +651,22 @@ PRBool nsCaret::MustDrawCaret()
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
DrawCaretWithContext DrawCaret
By this point, the caret rect should have been set up.
----------------------------------------------------------------------------- */ ----------------------------------------------------------------------------- */
void nsCaret::DrawCaretWithContext(nsIRenderingContext* inRendContext) void nsCaret::DrawCaret()
{ {
// do we need to draw the caret at all?
if (!MustDrawCaret())
return;
// if we are drawing, not erasing, then set up the frame etc.
if (!mDrawn)
{
if (!SetupDrawingFrameAndOffset())
return;
}
NS_ASSERTION(mLastCaretFrame != nsnull, "Should have a frame here"); NS_ASSERTION(mLastCaretFrame != nsnull, "Should have a frame here");
@ -658,37 +691,44 @@ void nsCaret::DrawCaretWithContext(nsIRenderingContext* inRendContext)
frameRect += viewOffset; frameRect += viewOffset;
nsCOMPtr<nsIPresContext> presContext;
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell); nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
if (presShell) if (!presShell) return;
{
if (NS_FAILED(presShell->GetPresContext(getter_AddRefs(presContext)))) nsCOMPtr<nsIPresContext> presContext;
return; if (NS_FAILED(presShell->GetPresContext(getter_AddRefs(presContext))))
}
else
return; return;
// make a rendering context, if we didn't get passed one // if the view changed, or we don't have a rendering context, make one
nsCOMPtr<nsIRenderingContext> localRC = do_QueryInterface(inRendContext); // OK if inRendContext is null // because of drawing issues, always make a new RC at the momemt. See bug 28068
if (!localRC) if (
#ifdef DONT_REUSE_RENDERING_CONTEXT
PR_TRUE ||
#endif
(mLastCaretView != drawingView) || !mRendContext)
{ {
mRendContext = nsnull; // free existing one if we have one
nsCOMPtr<nsIDeviceContext> dx; nsCOMPtr<nsIDeviceContext> dx;
if (NS_FAILED(presContext->GetDeviceContext(getter_AddRefs(dx))) || !dx) if (NS_FAILED(presContext->GetDeviceContext(getter_AddRefs(dx))) || !dx)
return; return;
if (NS_FAILED(dx->CreateRenderingContext(drawingView, *getter_AddRefs(localRC))) || !localRC) if (NS_FAILED(dx->CreateRenderingContext(drawingView, *getter_AddRefs(mRendContext))) || !mRendContext)
return; return;
} }
// push a known good state
mRendContext->PushState();
// views are not refcounted
mLastCaretView = drawingView;
if (!mDrawn) if (!mDrawn)
{ {
nsPoint framePos(0, 0); nsPoint framePos(0, 0);
nsRect caretRect = frameRect; nsRect caretRect = frameRect;
mLastCaretFrame->GetPointFromOffset(presContext, localRC, mLastContentOffset, &framePos); mLastCaretFrame->GetPointFromOffset(presContext, mRendContext, mLastContentOffset, &framePos);
caretRect += framePos; caretRect += framePos;
//printf("Content offset %ld, frame offset %ld\n", focusOffset, framePos.x); //printf("Content offset %ld, frame offset %ld\n", focusOffset, framePos.x);
if(mCaretTwipsWidth < 0) if(mCaretTwipsWidth < 0)
@ -721,52 +761,17 @@ void nsCaret::DrawCaretWithContext(nsIRenderingContext* inRendContext)
inRendContext.SetColor(NS_RGB(85, 85, 85)); // we are drawing it; gray inRendContext.SetColor(NS_RGB(85, 85, 85)); // we are drawing it; gray
*/ */
localRC->SetColor(NS_RGB(255,255,255)); mRendContext->SetColor(NS_RGB(255,255,255));
localRC->InvertRect(mCaretRect); mRendContext->InvertRect(mCaretRect);
PRBool emptyClip; // I know what you're thinking. "Did he fire six shots or only five?"
mRendContext->PopState(emptyClip);
ToggleDrawnStatus(); ToggleDrawnStatus();
} #ifdef DONT_REUSE_RENDERING_CONTEXT
mRendContext = nsnull;
//----------------------------------------------------------------------------- #endif
void nsCaret::DrawCaret()
{
// do we need to draw the caret at all?
if (!MustDrawCaret())
return;
// if we are drawing, not erasing, then set up the frame etc.
if (!mDrawn)
{
if (! SetupDrawingFrameAndOffset())
return;
}
DrawCaretWithContext(nsnull);
}
//-----------------------------------------------------------------------------
void nsCaret::RefreshDrawCaret(nsIView *aView, nsIRenderingContext& inRendContext, const nsRect& aDirtyRect)
{
/*
if (! SetupDrawingFrameAndOffset())
return;
NS_ASSERTION(mLastCaretFrame != nsnull, "Should have a frame here");
nsPoint viewOffset(0, 0);
nsIView *drawingView;
//GetViewForRendering(viewOffset, drawingView);
mLastCaretFrame->GetOffsetFromView(viewOffset, &drawingView);
// are we in the view that is being painted?
if (drawingView == nsnull || drawingView != aView)
return;
mDrawn = PR_FALSE; // we're rendering to a view that is being redrawn
DrawCaretWithContext(inRendContext);
*/
} }
#ifdef XP_MAC #ifdef XP_MAC

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

@ -24,12 +24,13 @@
#include "nsCoord.h" #include "nsCoord.h"
#include "nsIDOMSelectionListener.h" #include "nsIDOMSelectionListener.h"
#include "nsIRenderingContext.h"
#include "nsITimer.h"
#include "nsICaret.h" #include "nsICaret.h"
#include "nsWeakPtr.h" #include "nsWeakPtr.h"
class nsITimer;
class nsIView; class nsIView;
class nsIRenderingContext;
class nsISelectionController; class nsISelectionController;
// {E14B66F6-BFC5-11d2-B57E-00105AA83B2F} // {E14B66F6-BFC5-11d2-B57E-00105AA83B2F}
@ -83,30 +84,28 @@ class nsCaret : public nsICaret,
void GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordType, nsPoint &viewOffset, nsRect& outClipRect, nsIView* &outView); void GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordType, nsPoint &viewOffset, nsRect& outClipRect, nsIView* &outView);
PRBool SetupDrawingFrameAndOffset(); PRBool SetupDrawingFrameAndOffset();
PRBool MustDrawCaret(); PRBool MustDrawCaret();
void RefreshDrawCaret(nsIView *aView, nsIRenderingContext& inRendContext, const nsRect& aDirtyRect);
void DrawCaretWithContext(nsIRenderingContext* inRendContext);
void DrawCaret(); void DrawCaret();
void ToggleDrawnStatus() { mDrawn = !mDrawn; } void ToggleDrawnStatus() { mDrawn = !mDrawn; }
nsCOMPtr<nsIWeakReference> mPresShell; protected:
nsCOMPtr<nsITimer> mBlinkTimer; nsWeakPtr mPresShell;
nsWeakPtr mDomSelectionWeak;
PRUint32 mBlinkRate; // time for one cyle (off then on), in milliseconds nsCOMPtr<nsITimer> mBlinkTimer;
nscoord mCaretTwipsWidth; // caret width in twips nsCOMPtr<nsIRenderingContext> mRendContext;
nscoord mCaretPixelsWidth; // caret width in pixels
PRUint32 mBlinkRate; // time for one cyle (off then on), in milliseconds
nscoord mCaretTwipsWidth; // caret width in twips
nscoord mCaretPixelsWidth; // caret width in pixels
PRBool mVisible; // is the caret blinking PRPackedBool mVisible; // is the caret blinking
PRBool mReadOnly; // it the caret in readonly state (draws differently) PRPackedBool mDrawn; // this should be mutable
PRPackedBool mReadOnly; // it the caret in readonly state (draws differently)
private:
PRBool mDrawn; // this should be mutable
nsRect mCaretRect; // the last caret rect nsRect mCaretRect; // the last caret rect
nsIFrame* mLastCaretFrame; // store the frame the caret was last drawn in. nsIFrame* mLastCaretFrame; // store the frame the caret was last drawn in.
nsIView* mLastCaretView; // last view that we used for drawing. Cached so we can tell when we need to make a new RC
PRInt32 mLastContentOffset; PRInt32 mLastContentOffset;
nsWeakPtr mDomSelectionWeak;
}; };