Bug 238493. BeginUpdateViewBatch should suppress widget geometry changes and make them happen when the batch ends. r+sr=bzbarsky

This commit is contained in:
roc+%cs.cmu.edu 2004-10-11 20:35:51 +00:00
parent 1953bdd78f
commit cc07de278b
10 changed files with 116 additions and 256 deletions

View File

@ -147,6 +147,8 @@ struct NS_GFX nsRect {
nsPoint BottomLeft() const { return nsPoint(x, YMost()); }
nsPoint BottomRight() const { return nsPoint(XMost(), YMost()); }
nsSize Size() const { return nsSize(width, height); }
// Helper methods for computing the extents
nscoord XMost() const {return x + width;}
nscoord YMost() const {return y + height;}

View File

@ -1333,7 +1333,7 @@ protected:
nsresult AddDummyLayoutRequest(void);
nsresult RemoveDummyLayoutRequest(void);
nsresult WillCauseReflow();
void WillCauseReflow() {}
nsresult DidCauseReflow();
void DidDoReflow();
nsresult ProcessReflowCommands(PRBool aInterruptible);
@ -6171,20 +6171,9 @@ PresShell::PostReflowEvent()
}
}
nsresult
PresShell::WillCauseReflow()
{
mViewManager->CacheWidgetChanges(PR_TRUE);
return NS_OK;
}
nsresult
PresShell::DidCauseReflow()
{
if (mViewManager) {
mViewManager->CacheWidgetChanges(PR_FALSE);
}
// We may have had more reflow commands appended to the queue during
// our reflow. Make sure these get processed at some point.
if (!gAsyncReflowDuringDocLoad && mDocumentLoading) {

View File

@ -1333,7 +1333,7 @@ protected:
nsresult AddDummyLayoutRequest(void);
nsresult RemoveDummyLayoutRequest(void);
nsresult WillCauseReflow();
void WillCauseReflow() {}
nsresult DidCauseReflow();
void DidDoReflow();
nsresult ProcessReflowCommands(PRBool aInterruptible);
@ -6171,20 +6171,9 @@ PresShell::PostReflowEvent()
}
}
nsresult
PresShell::WillCauseReflow()
{
mViewManager->CacheWidgetChanges(PR_TRUE);
return NS_OK;
}
nsresult
PresShell::DidCauseReflow()
{
if (mViewManager) {
mViewManager->CacheWidgetChanges(PR_FALSE);
}
// We may have had more reflow commands appended to the queue during
// our reflow. Make sure these get processed at some point.
if (!gAsyncReflowDuringDocLoad && mDocumentLoading) {

View File

@ -63,57 +63,31 @@ enum nsViewVisibility {
{ 0xf0a21c40, 0xa7e1, 0x11d1, \
{ 0xa8, 0x24, 0x00, 0x40, 0x95, 0x9a, 0x28, 0xc9 } }
//Flag to determine whether the view will check if events can be handled
//by its children or just handle the events itself
#define NS_VIEW_FLAG_DONT_CHECK_CHILDREN 0x0001
// Public view flags are defined in this file
#define NS_VIEW_FLAGS_PUBLIC 0x00FF
// Private view flags are private to the view module,
// and are defined in nsView.h
#define NS_VIEW_FLAGS_PRIVATE 0xFF00
// indicates that the view is or contains a placeholder view
#define NS_VIEW_FLAG_CONTAINS_PLACEHOLDER 0x0002
// Public view flags
//the view is transparent
#define NS_VIEW_FLAG_TRANSPARENT 0x0004
// The view is transparent
#define NS_VIEW_FLAG_TRANSPARENT 0x0001
// The view is always painted onto a background consisting
// of a uniform field of opaque pixels.
#define NS_VIEW_FLAG_UNIFORM_BACKGROUND 0x0008
#define NS_VIEW_FLAG_UNIFORM_BACKGROUND 0x0002
//indicates that the view should not be bitblt'd when moved
//or scrolled and instead must be repainted
#define NS_VIEW_FLAG_DONT_BITBLT 0x0010
// Indicates that the view is using auto z-indexing
#define NS_VIEW_FLAG_AUTO_ZINDEX 0x0004
// indicates that the view is using auto z-indexing
#define NS_VIEW_FLAG_AUTO_ZINDEX 0x0020
// Indicates that the view is a floating view.
#define NS_VIEW_FLAG_FLOATING 0x0008
// indicates that the view is a floating view.
#define NS_VIEW_FLAG_FLOATING 0x0040
// set if our widget resized.
#define NS_VIEW_FLAG_WIDGET_RESIZED 0x0080
// set if our widget moved.
#define NS_VIEW_FLAG_WIDGET_MOVED 0x0100
// set if this view is clipping its normal descendants
// to its bounds. When this flag is set, child views
// bounds need not be inside this view's bounds.
#define NS_VIEW_FLAG_CLIP_CHILDREN_TO_BOUNDS 0x0200
// set if this view is clipping its descendants (including
// placeholders) to its bounds
#define NS_VIEW_FLAG_CLIP_PLACEHOLDERS_TO_BOUNDS 0x0400
// set if this view is clipping its normal descendants to
// a specified region. When this flag is set, child views
// bounds need not be inside this view's bounds. The region
// will always lie inside this view's bounds.
// #define NS_VIEW_FLAG_CLIP_CHILDREN_TO_REGION 0x0800
// we don't need this flag; we just check whether mClipRect
// is null
// if set it indicates that this view should be
// If set it indicates that this view should be
// displayed above z-index:auto views if this view
// is z-index:auto also
#define NS_VIEW_FLAG_TOPMOST 0x0800
#define NS_VIEW_FLAG_TOPMOST 0x0010
struct nsViewZIndex {
PRBool mIsAuto;

View File

@ -439,17 +439,6 @@ public:
*/
NS_IMETHOD ForceUpdate() = 0;
/**
* Turn widget on or off widget movement caching
*/
NS_IMETHOD IsCachingWidgetChanges(PRBool* aCaching)=0;
/**
* Pass true to cache widget changes. pass false to stop. When false is passed
* All widget changes will be applied.
*/
NS_IMETHOD CacheWidgetChanges(PRBool aCache)=0;
/**
* Control double buffering of the display. If double buffering
* is enabled the viewmanager is allowed to render to an offscreen

View File

@ -368,7 +368,7 @@ void nsView::SetPosition(nscoord aX, nscoord aY)
NS_ASSERTION(GetParent() || (aX == 0 && aY == 0),
"Don't try to move the root widget to something non-zero");
ResetWidgetPosition(PR_TRUE);
ResetWidgetBounds(PR_TRUE, PR_TRUE, PR_FALSE);
}
void nsView::SetPositionIgnoringChildWidgets(nscoord aX, nscoord aY)
@ -378,27 +378,30 @@ void nsView::SetPositionIgnoringChildWidgets(nscoord aX, nscoord aY)
mPosX = aX;
mPosY = aY;
ResetWidgetPosition(PR_FALSE);
ResetWidgetBounds(PR_FALSE, PR_TRUE, PR_FALSE);
}
void nsView::ResetWidgetPosition(PRBool aRecurse) {
if (mWindow)
{
// see if we are caching our widget changes. Yes?
// mark us as changed. Later we will actually move the
// widget.
PRBool caching = PR_FALSE;
mViewManager->IsCachingWidgetChanges(&caching);
if (caching) {
mVFlags |= NS_VIEW_FLAG_WIDGET_MOVED;
void nsView::ResetWidgetBounds(PRBool aRecurse, PRBool aMoveOnly,
PRBool aInvalidateChangedSize) {
if (mWindow) {
// If our view manager has refresh disabled, then
// do nothing; the view manager will set our position when
// refresh is reenabled.
if (!mViewManager->IsRefreshEnabled()) {
return;
}
// The geometry of a root view's widget is controlled externally,
// NOT by sizing or positioning the view
if (mViewManager->GetRootView() == this) {
return;
}
nsIDeviceContext *dx;
float scale;
float t2p;
mViewManager->GetDeviceContext(dx);
scale = dx->AppUnitsToDevUnits();
t2p = dx->AppUnitsToDevUnits();
NS_RELEASE(dx);
nsPoint offset(0, 0);
@ -406,113 +409,55 @@ void nsView::ResetWidgetPosition(PRBool aRecurse) {
GetParent()->GetNearestWidget(&offset);
}
mWindow->Move(NSTwipsToIntPixels((mDimBounds.x + offset.x), scale),
NSTwipsToIntPixels((mDimBounds.y + offset.y), scale));
nsRect newBounds(NSTwipsToIntPixels((mDimBounds.x + offset.x), t2p),
NSTwipsToIntPixels((mDimBounds.y + offset.y), t2p),
NSTwipsToIntPixels(mDimBounds.width, t2p),
NSTwipsToIntPixels(mDimBounds.height, t2p));
PRBool changedPos = PR_TRUE;
PRBool changedSize = PR_TRUE;
if (!(mVFlags & NS_VIEW_FLAG_HAS_POSITIONED_WIDGET)) {
mVFlags |= NS_VIEW_FLAG_HAS_POSITIONED_WIDGET;
} else {
nsRect curBounds;
mWindow->GetBounds(curBounds);
changedPos = curBounds.TopLeft() != newBounds.TopLeft();
changedSize = curBounds.Size() != newBounds.Size();
}
if (changedPos) {
if (changedSize && !aMoveOnly) {
mWindow->Resize(newBounds.x, newBounds.y, newBounds.width, newBounds.height,
aInvalidateChangedSize);
} else {
mWindow->Move(newBounds.x, newBounds.y);
}
} else {
if (changedSize && !aMoveOnly) {
mWindow->Resize(newBounds.width, newBounds.height, aInvalidateChangedSize);
} // else do nothing!
}
} else if (aRecurse) {
// reposition any widgets under this view
for (nsView* v = GetFirstChild(); v; v = v->GetNextSibling()) {
v->ResetWidgetPosition(aRecurse);
v->ResetWidgetBounds(PR_TRUE, aMoveOnly, aInvalidateChangedSize);
}
}
}
NS_IMETHODIMP nsView::SynchWidgetSizePosition()
{
// if the widget was moved or resized
if (mVFlags & NS_VIEW_FLAG_WIDGET_MOVED || mVFlags & NS_VIEW_FLAG_WIDGET_RESIZED)
{
nsIDeviceContext *dx;
float t2p;
mViewManager->GetDeviceContext(dx);
t2p = dx->AppUnitsToDevUnits();
NS_RELEASE(dx);
// if we just resized do it
if (mVFlags & NS_VIEW_FLAG_WIDGET_RESIZED)
{
PRInt32 width = NSTwipsToIntPixels(mDimBounds.width, t2p);
PRInt32 height = NSTwipsToIntPixels(mDimBounds.height, t2p);
nsRect bounds;
mWindow->GetBounds(bounds);
if (bounds.width != width || bounds.height != bounds.height) {
mWindow->Resize(width,height, PR_TRUE);
}
mVFlags &= ~NS_VIEW_FLAG_WIDGET_RESIZED;
}
if (mVFlags & NS_VIEW_FLAG_WIDGET_MOVED) {
// if we just moved do it.
nsPoint offset;
GetParent()->GetNearestWidget(&offset);
PRInt32 x = NSTwipsToIntPixels(mDimBounds.x + offset.x, t2p);
PRInt32 y = NSTwipsToIntPixels(mDimBounds.y + offset.y, t2p);
nsRect bounds;
mWindow->GetBounds(bounds);
if (bounds.x != x || bounds.y != y) {
mWindow->Move(x,y);
}
mVFlags &= ~NS_VIEW_FLAG_WIDGET_MOVED;
}
}
return NS_OK;
}
void nsView::SetDimensions(const nsRect& aRect, PRBool aPaint)
void nsView::SetDimensions(const nsRect& aRect, PRBool aPaint, PRBool aResizeWidget)
{
nsRect dims = aRect;
dims.MoveBy(mPosX, mPosY);
if (mDimBounds.x == dims.x && mDimBounds.y == dims.y && mDimBounds.width == dims.width
&& mDimBounds.height == dims.height) {
if (mDimBounds == dims) {
return;
}
if (nsnull == mWindow)
{
mDimBounds = dims;
}
else
{
PRBool needToMoveWidget = mDimBounds.x != dims.x || mDimBounds.y != dims.y;
mDimBounds = dims;
mDimBounds = dims;
PRBool caching = PR_FALSE;
mViewManager->IsCachingWidgetChanges(&caching);
if (caching) {
mVFlags |= NS_VIEW_FLAG_WIDGET_RESIZED | (needToMoveWidget ? NS_VIEW_FLAG_WIDGET_MOVED : 0);
return;
}
nsIDeviceContext *dx;
float t2p;
mViewManager->GetDeviceContext(dx);
t2p = dx->AppUnitsToDevUnits();
if (needToMoveWidget) {
NS_ASSERTION(GetParent(), "Don't try to move the root widget, dude");
nsPoint offset;
GetParent()->GetNearestWidget(&offset);
mWindow->Move(NSTwipsToIntPixels((mDimBounds.x + offset.x), t2p),
NSTwipsToIntPixels((mDimBounds.y + offset.y), t2p));
}
mWindow->Resize(NSTwipsToIntPixels(mDimBounds.width, t2p), NSTwipsToIntPixels(mDimBounds.height, t2p),
aPaint);
NS_RELEASE(dx);
if (aResizeWidget) {
ResetWidgetBounds(PR_FALSE, PR_FALSE, aPaint);
}
}
@ -716,9 +661,8 @@ NS_IMETHODIMP nsView::SetWidget(nsIWidget *aWidget)
// Destroy any old wrappers if there are any
ViewWrapper* oldWrapper = GetWrapperFor(aWidget);
NS_IF_RELEASE(oldWrapper);
NS_IF_RELEASE(mWindow);
mWindow = aWidget;
if (nsnull != mWindow)
@ -727,6 +671,8 @@ NS_IMETHODIMP nsView::SetWidget(nsIWidget *aWidget)
mWindow->SetClientData(wrapper);
}
mVFlags &= ~NS_VIEW_FLAG_HAS_POSITIONED_WIDGET;
return NS_OK;
}
@ -749,6 +695,8 @@ nsresult nsView::LoadWidget(const nsCID &aClassIID)
delete wrapper;
}
mVFlags &= ~NS_VIEW_FLAG_HAS_POSITIONED_WIDGET;
return rv;
}

View File

@ -55,6 +55,31 @@ class nsIViewManager;
class nsViewManager;
class nsZPlaceholderView;
// View flags private to the view module
// indicates that the view is or contains a placeholder view
#define NS_VIEW_FLAG_CONTAINS_PLACEHOLDER 0x0100
// Flag to determine whether the view will check if events can be handled
// by its children or just handle the events itself
#define NS_VIEW_FLAG_DONT_CHECK_CHILDREN 0x0200
// indicates that the view should not be bitblt'd when moved
// or scrolled and instead must be repainted
#define NS_VIEW_FLAG_DONT_BITBLT 0x0400
// set if this view is clipping its normal descendants
// to its bounds. When this flag is set, child views
// bounds need not be inside this view's bounds.
#define NS_VIEW_FLAG_CLIP_CHILDREN_TO_BOUNDS 0x0800
// set if this view is clipping its descendants (including
// placeholders) to its bounds
#define NS_VIEW_FLAG_CLIP_PLACEHOLDERS_TO_BOUNDS 0x1000
// set if this view has positioned its widget at least once
#define NS_VIEW_FLAG_HAS_POSITIONED_WIDGET 0x2000
class nsView : public nsIView
{
public:
@ -99,7 +124,8 @@ public:
* The x and y coordinates may be < 0, indicating that the view extends above
* or to the left of its origin position.
*/
virtual void SetDimensions(const nsRect &aRect, PRBool aPaint = PR_TRUE);
virtual void SetDimensions(const nsRect &aRect, PRBool aPaint = PR_TRUE,
PRBool aResizeWidget = PR_TRUE);
void GetDimensions(nsRect &aRect) const { aRect = mDimBounds; aRect.x -= mPosX; aRect.y -= mPosY; }
void GetDimensions(nsSize &aSize) const { aSize.width = mDimBounds.width; aSize.height = mDimBounds.height; }
@ -206,12 +232,6 @@ public:
*/
nsRect GetClippedRect();
/**
* Sync your widget size and position with the view
*/
NS_IMETHOD SynchWidgetSizePosition();
// Helper function to get the view that's associated with a widget
static nsView* GetViewFor(nsIWidget* aWidget);
@ -257,7 +277,7 @@ public:
// Just write "pt -= view->GetPosition();"
// When everything's converted to nsPoint, this can go away.
void ConvertFromParentCoords(nscoord* aX, nscoord* aY) const { *aX -= mPosX; *aY -= mPosY; }
void ResetWidgetPosition(PRBool aRecurse);
void ResetWidgetBounds(PRBool aRecurse, PRBool aMoveOnly, PRBool aInvalidateChangedSize);
void SetPositionIgnoringChildWidgets(nscoord aX, nscoord aY);
nsresult LoadWidget(const nsCID &aClassIID);

View File

@ -457,7 +457,6 @@ nsViewManager::nsViewManager()
mVMCount++;
// NOTE: we use a zeroing operator new, so all data members are
// assumed to be cleared here.
mCachingWidgetChanges = 0;
mDefaultBackgroundColor = NS_RGBA(0, 0, 0, 0);
mAllowDoubleBuffering = PR_TRUE;
mHasPendingInvalidates = PR_FALSE;
@ -626,7 +625,8 @@ NS_IMETHODIMP nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight
// Resize the root view
if (nsnull != mRootView) {
nsRect dim(0, 0, aWidth, aHeight);
mRootView->SetDimensions(dim);
// Don't resize the widget. It is already being set elsewhere.
mRootView->SetDimensions(dim, PR_TRUE, PR_FALSE);
}
//printf("new dims: %d %d\n", aWidth, aHeight);
@ -1528,7 +1528,10 @@ void nsViewManager::ProcessPendingUpdates(nsView* aView)
if (!aView) {
return;
}
if (aView->HasWidget()) {
aView->ResetWidgetBounds(PR_FALSE, PR_FALSE, PR_TRUE);
nsCOMPtr<nsIRegion> dirtyRegion;
aView->GetDirtyRegion(*getter_AddRefs(dirtyRegion));
if (dirtyRegion && !dirtyRegion->IsEmpty()) {
@ -4007,38 +4010,6 @@ NS_IMETHODIMP nsViewManager::GetRectVisibility(nsIView *aView,
}
NS_IMETHODIMP
nsViewManager::IsCachingWidgetChanges(PRBool* aCaching)
{
#ifdef CACHE_WIDGET_CHANGES
*aCaching = (mCachingWidgetChanges > 0);
#else
*aCaching = PR_FALSE;
#endif
return NS_OK;
}
NS_IMETHODIMP
nsViewManager::CacheWidgetChanges(PRBool aCache)
{
#ifdef CACHE_WIDGET_CHANGES
if (aCache == PR_TRUE)
mCachingWidgetChanges++;
else
mCachingWidgetChanges--;
NS_ASSERTION(mCachingWidgetChanges >= 0, "One too many decrements");
// if we turned it off. Then move and size all the widgets.
if (mCachingWidgetChanges == 0)
ProcessWidgetChanges(mRootView);
#endif
return NS_OK;
}
NS_IMETHODIMP
nsViewManager::AllowDoubleBuffering(PRBool aDoubleBuffer)
{
@ -4070,30 +4041,6 @@ nsViewManager::ProcessInvalidateEvent()
mInvalidateEventQueue = nsnull;
}
nsresult
nsViewManager::ProcessWidgetChanges(nsView* aView)
{
//printf("---------Begin Sync----------\n");
nsresult rv = aView->SynchWidgetSizePosition();
if (NS_FAILED(rv))
return rv;
nsView *child = aView->GetFirstChild();
while (nsnull != child) {
if (child->GetViewManager() == this) {
rv = ProcessWidgetChanges(child);
if (NS_FAILED(rv))
return rv;
}
child = child->GetNextSibling();
}
//printf("---------End Sync----------\n");
return NS_OK;
}
NS_IMETHODIMP
nsViewManager::SetDefaultBackgroundColor(nscolor aColor)
{

View File

@ -202,8 +202,6 @@ public:
nsIWidget* GetWidget() { return mRootView ? mRootView->GetWidget() : nsnull; }
NS_IMETHOD ForceUpdate();
NS_IMETHOD IsCachingWidgetChanges(PRBool* aCaching);
NS_IMETHOD CacheWidgetChanges(PRBool aCache);
NS_IMETHOD AllowDoubleBuffering(PRBool aDoubleBuffer);
NS_IMETHOD IsPainting(PRBool& aIsPainting);
NS_IMETHOD FlushPendingInvalidates();
@ -344,8 +342,6 @@ private:
nsresult GetVisibleRect(nsRect& aVisibleRect);
nsresult ProcessWidgetChanges(nsView* aView);
// Utilities used to size the offscreen drawing surface
/**
@ -382,6 +378,8 @@ public: // NOT in nsIViewManager, so private to the view module
// not be in this view manager).
static nsPoint ComputeViewOffset(const nsView *aView);
PRBool IsRefreshEnabled() { return mRefreshEnabled; }
private:
nsIDeviceContext *mContext;
float mTwipsToPixels;
@ -393,7 +391,6 @@ private:
PRInt32 mUpdateBatchCnt;
PRUint32 mUpdateBatchFlags;
nsIScrollableView *mRootScrollable;
PRInt32 mCachingWidgetChanges;
nscolor mDefaultBackgroundColor;
nsPoint mMouseLocation; // device units, relative to mRootView
nsCOMPtr<nsIBlender> mBlender;

View File

@ -278,7 +278,12 @@ nsCommonWidget::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
if (AreBoundsSane()) {
// Yep? Resize the window
//Maybe, the toplevel has moved
if (mIsTopLevel)
// Note that if the widget needs to be shown because it
// was previously insane in Resize(x,y,w,h), then we need
// to set the x and y here too, because the widget wasn't
// moved back then
if (mIsTopLevel || mNeedsShow)
NativeResize(mBounds.x, mBounds.y,
mBounds.width, mBounds.height, aRepaint);
else