Bug 352093. Part 15: Rework scrolling so that we don't need a dedicated native widget for scrollframes. r=dbaron,joshmoz,karlt,jmathies

This commit is contained in:
Robert O'Callahan 2009-07-22 12:45:13 +12:00
parent a00da77ddf
commit 5fa812e479
20 changed files with 257 additions and 351 deletions

View File

@ -93,7 +93,8 @@ public:
// nsIScrollPositionListener
NS_IMETHOD ScrollPositionWillChange(nsIScrollableView *aView,
nscoord aX, nscoord aY);
virtual void ViewPositionDidChange(nsIScrollableView* aScrollable) {}
virtual void ViewPositionDidChange(nsIScrollableView* aScrollable,
nsTArray<nsIWidget::Configuration>* aConfigurations) {}
NS_IMETHOD ScrollPositionDidChange(nsIScrollableView *aView,
nscoord aX, nscoord aY);

View File

@ -1381,40 +1381,6 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder,
return NS_OK;
}
PRBool
nsGfxScrollFrameInner::NeedsClipWidget() const
{
// Scrollports contained in form controls (e.g., listboxes) don't get
// widgets. Also, transformed elements don't need clip widgets since they
// result in graphical glitches.
for (nsIFrame* parentFrame = mOuter; parentFrame;
parentFrame = nsLayoutUtils::GetCrossDocParentFrame(parentFrame)) {
/* See if we have a transform... we should have no widget if that's the case. */
if (parentFrame->GetStyleDisplay()->HasTransform())
return PR_FALSE;
/* If we're a form element, we don't need a widget. */
nsIFormControlFrame* fcFrame = do_QueryFrame(parentFrame);
if (fcFrame) {
return PR_FALSE;
}
}
// Scrollports that don't ever show associated scrollbars don't get
// widgets, because they will seldom actually be scrolled.
nsIScrollableFrame *scrollableFrame = do_QueryFrame(mOuter);
ScrollbarStyles scrollbars = scrollableFrame->GetScrollbarStyles();
if ((scrollbars.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN
|| scrollbars.mHorizontal == NS_STYLE_OVERFLOW_VISIBLE)
&& (scrollbars.mVertical == NS_STYLE_OVERFLOW_HIDDEN
|| scrollbars.mVertical == NS_STYLE_OVERFLOW_VISIBLE)) {
return PR_FALSE;
}
return PR_TRUE;
}
void
nsGfxScrollFrameInner::CreateScrollableView()
{
@ -1429,11 +1395,6 @@ nsGfxScrollFrameInner::CreateScrollableView()
// Insert the view into the view hierarchy
viewManager->InsertChild(outerView, view, nsnull, PR_TRUE);
// Have the scrolling view create its internal widgets
if (NeedsClipWidget()) {
mScrollableView->CreateScrollControls();
}
}
static void HandleScrollPref(nsIScrollable *aScrollable, PRInt32 aOrientation,
@ -1792,11 +1753,15 @@ nsGfxScrollFrameInner::InternalScrollPositionDidChange(nscoord aX, nscoord aY)
}
void
nsGfxScrollFrameInner::ViewPositionDidChange(nsIScrollableView* aScrollable)
nsGfxScrollFrameInner::ViewPositionDidChange(nsIScrollableView* aScrollable,
nsTArray<nsIWidget::Configuration>* aConfigurations)
{
// Update frame position to match view offsets
nsPoint childOffset = mScrolledFrame->GetView()->GetOffsetTo(mOuter->GetView());
mScrolledFrame->SetPosition(childOffset);
mOuter->PresContext()->RootPresContext()->
GetPluginGeometryUpdates(mOuter, aConfigurations);
}
/**
@ -1817,8 +1782,6 @@ nsGfxScrollFrameInner::ScrollPositionDidChange(nsIScrollableView* aScrollable, n
mOuter->InvalidateWithFlags(nsRect(nsPoint(0, 0), mOuter->GetSize()),
nsIFrame::INVALIDATE_NOTIFY_ONLY);
mOuter->PresContext()->RootPresContext()->UpdatePluginGeometry(mOuter);
return NS_OK;
}

View File

@ -79,7 +79,6 @@ public:
// reload our child frame list.
// We need this if a scrollbar frame is recreated.
void ReloadChildFrames();
PRBool NeedsClipWidget() const;
void CreateScrollableView();
nsresult CreateAnonymousContent(nsTArray<nsIContent*>& aElements);
@ -98,7 +97,8 @@ public:
// nsIScrollPositionListener
NS_IMETHOD ScrollPositionWillChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY);
virtual void ViewPositionDidChange(nsIScrollableView* aScrollable);
virtual void ViewPositionDidChange(nsIScrollableView* aScrollable,
nsTArray<nsIWidget::Configuration>* aConfigurations);
NS_IMETHOD ScrollPositionDidChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY);
// This gets called when the 'curpos' attribute on one of the scrollbars changes

View File

@ -130,7 +130,8 @@ public:
// nsIScrollPositionListener
NS_IMETHOD ScrollPositionWillChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY);
virtual void ViewPositionDidChange(nsIScrollableView* aScrollable) {}
virtual void ViewPositionDidChange(nsIScrollableView* aScrollable,
nsTArray<nsIWidget::Configuration>* aConfigurations) {}
NS_IMETHOD ScrollPositionDidChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY);
// nsICanvasFrame

View File

@ -362,7 +362,8 @@ public:
// nsIScrollPositionListener interface
NS_IMETHOD ScrollPositionWillChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY);
virtual void ViewPositionDidChange(nsIScrollableView* aScrollable) {}
virtual void ViewPositionDidChange(nsIScrollableView* aScrollable,
nsTArray<nsIWidget::Configuration>* aConfigurations) {}
NS_IMETHOD ScrollPositionDidChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY);
//locals

View File

@ -4128,7 +4128,24 @@ nsTreeBodyFrame::ScrollInternal(const ScrollParts& aParts, PRInt32 aRow)
if (widget) {
nscoord rowHeightAsPixels =
PresContext()->AppUnitsToDevPixels(mRowHeight);
widget->Scroll(0, -delta*rowHeightAsPixels, nsnull);
nsIntPoint deltaPt = nsIntPoint(0, -delta*rowHeightAsPixels);
nsIntRect bounds;
widget->GetBounds(bounds);
bounds.x = bounds.y = 0;
nsIntRect source;
source.IntersectRect(bounds, bounds - deltaPt);
// No plugins have a tree widget as a parent so we don't need
// configurations here.
nsTArray<nsIWidget::Configuration> emptyConfigurations;
widget->Scroll(deltaPt, source, emptyConfigurations);
nsIntRect invalid = bounds;
if (deltaPt.y < 0) {
invalid.y = bounds.height + deltaPt.y;
invalid.height = -deltaPt.y;
} else {
invalid.height = deltaPt.y;
}
widget->Invalidate(invalid, PR_FALSE);
}
}
@ -4166,7 +4183,24 @@ nsTreeBodyFrame::ScrollHorzInternal(const ScrollParts& aParts, PRInt32 aPosition
} else {
nsIWidget* widget = nsLeafBoxFrame::GetView()->GetWidget();
if (widget) {
widget->Scroll(PresContext()->AppUnitsToDevPixels(-delta), 0, nsnull);
nsIntPoint deltaPt(PresContext()->AppUnitsToDevPixels(-delta), 0);
nsIntRect bounds;
widget->GetBounds(bounds);
bounds.x = bounds.y = 0;
nsIntRect source;
source.IntersectRect(bounds, bounds - deltaPt);
// No plugins have a tree widget as a parent so we don't need
// configurations here.
nsTArray<nsIWidget::Configuration> emptyConfigurations;
widget->Scroll(deltaPt, source, emptyConfigurations);
nsIntRect invalid = bounds;
if (deltaPt.x < 0) {
invalid.x = bounds.width + deltaPt.x;
invalid.width = -deltaPt.x;
} else {
invalid.width = deltaPt.x;
}
widget->Invalidate(invalid, PR_FALSE);
}
}

View File

@ -42,16 +42,16 @@
#include "nsISupports.h"
#include "nsCoord.h"
#include "nsTArray.h"
#include "nsIWidget.h"
// forward declarations
class nsIScrollableView;
// IID for the nsIScrollPositionListener interface
// {98a0c040-09cf-408b-b55f-321b4f8d9d67}
#define NS_ISCROLLPOSITIONLISTENER_IID \
{ 0x98a0c040, 0x09cf, 0x408b, \
{ 0xb5, 0x5f, 0x32, 0x1b, 0x4f, 0x8d, 0x9d, 0x67 } }
{ 0x9654a477, 0x49a7, 0x4aea, \
{ 0xb7, 0xe3, 0x90, 0xe5, 0xe5, 0xd4, 0x28, 0xcd } }
/**
* Provides a way for a client of an nsIScrollableView to learn about scroll position
@ -62,7 +62,11 @@ public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCROLLPOSITIONLISTENER_IID)
NS_IMETHOD ScrollPositionWillChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY) = 0;
virtual void ViewPositionDidChange(nsIScrollableView* aScrollable) = 0;
// The scrollframe implementation of this method appends a list of widget
// configuration requests to aConfigurations. No other implementor
// should touch it.
virtual void ViewPositionDidChange(nsIScrollableView* aScrollable,
nsTArray<nsIWidget::Configuration>* aConfigurations) = 0;
NS_IMETHOD ScrollPositionDidChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY) = 0;
};

View File

@ -64,14 +64,6 @@ class nsIScrollableView {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCROLLABLEVIEW_IID)
/**
* Create the controls used to allow scrolling. Call this method
* before anything else is done with the scrollable view.
* @param aNative native widget to use as parent for control widgets
* @return error status
*/
NS_IMETHOD CreateScrollControls(nsNativeWidget aNative = nsnull) = 0;
/**
* Get the dimensions of the container
* @param aWidth return value for width of container

View File

@ -143,18 +143,6 @@ NS_IMETHODIMP nsScrollPortView::RemoveScrollPositionListener(nsIScrollPositionLi
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsScrollPortView::CreateScrollControls(nsNativeWidget aNative)
{
nsWidgetInitData initData;
initData.clipChildren = PR_TRUE;
initData.clipSiblings = PR_TRUE;
CreateWidget(kWidgetCID, &initData,
mWindow ? nsnull : aNative);
return NS_OK;
}
NS_IMETHODIMP nsScrollPortView::GetContainerSize(nscoord *aWidth, nscoord *aHeight) const
{
if (!aWidth || !aHeight)
@ -330,9 +318,9 @@ static void AdjustChildWidgets(nsView *aView,
widget->Show(PR_TRUE);
}
}
// Don't recurse if the view has a widget, because we adjusted the view's
// widget position, and its child widgets are relative to its position
} else {
// Don't recurse if the view haLs a widget, because we adjusted the view's
// widget position, and its child widgets are relative to its positon
nsPoint widgetToViewOrigin = aWidgetToParentViewOrigin
+ aView->GetPosition();
@ -553,8 +541,9 @@ NS_IMETHODIMP nsScrollPortView::CanScroll(PRBool aHorizontal,
return NS_OK;
}
void nsScrollPortView::Scroll(nsView *aScrolledView, nsPoint aTwipsDelta, nsIntPoint aPixDelta,
PRInt32 aP2A)
void nsScrollPortView::Scroll(nsView *aScrolledView, nsPoint aTwipsDelta,
nsIntPoint aPixDelta, PRInt32 aP2A,
const nsTArray<nsIWidget::Configuration>& aConfigurations)
{
if (aTwipsDelta.x != 0 || aTwipsDelta.y != 0)
{
@ -564,37 +553,20 @@ void nsScrollPortView::Scroll(nsView *aScrolledView, nsPoint aTwipsDelta, nsIntP
if (aScrolledView->NeedsInvalidateFrameOnScroll())
GetViewManager()->GetViewObserver()->InvalidateFrameForView(aScrolledView);
nsIWidget *scrollWidget = GetWidget();
nsPoint nearestWidgetOffset;
nsIWidget *nearestWidget = GetNearestWidget(&nearestWidgetOffset);
nsRegion updateRegion;
PRBool canBitBlit = scrollWidget &&
PRBool canBitBlit = nearestWidget &&
mViewManager->CanScrollWithBitBlt(aScrolledView, aTwipsDelta, &updateRegion) &&
scrollWidget->GetTransparencyMode() != eTransparencyTransparent;
nearestWidget->GetTransparencyMode() != eTransparencyTransparent;
if (canBitBlit) {
// We're going to bit-blit. Let the viewmanager know so it can
// adjust dirty regions appropriately.
mViewManager->WillBitBlit(this, aTwipsDelta);
}
if (!scrollWidget)
{
NS_ASSERTION(!canBitBlit, "Someone screwed up");
nsPoint offsetToWidget;
GetNearestWidget(&offsetToWidget);
// We're moving the child widgets because we are scrolling. But
// the child widgets may stick outside our bounds, so their area
// may include area that's not supposed to be scrolled. We need
// to invalidate to ensure that any such area is properly
// repainted back to the right rendering.
AdjustChildWidgets(aScrolledView, offsetToWidget, aP2A, PR_TRUE);
// If we don't have a scroll widget then we must just update.
// We should call this after fixing up the widget positions to be
// consistent with the view hierarchy.
mViewManager->UpdateView(this, NS_VMREFRESH_DEFERRED);
} else if (!canBitBlit) {
if (!canBitBlit) {
// We can't blit for some reason.
// Just update the view and adjust widgets
// Recall that our widget's origin is at our bounds' top-left
if (nearestWidget) {
nearestWidget->ConfigureChildren(aConfigurations);
}
nsRect bounds(GetBounds());
nsPoint topLeft(bounds.x, bounds.y);
AdjustChildWidgets(aScrolledView,
@ -602,34 +574,45 @@ void nsScrollPortView::Scroll(nsView *aScrolledView, nsPoint aTwipsDelta, nsIntP
// We should call this after fixing up the widget positions to be
// consistent with the view hierarchy.
mViewManager->UpdateView(this, NS_VMREFRESH_DEFERRED);
} else { // if we can blit and have a scrollwidget then scroll.
nsIntRect* toScrollPtr = nsnull;
} else {
// We're going to bit-blit. Let the viewmanager know so it can
// adjust dirty regions appropriately.
mViewManager->WillBitBlit(this, aTwipsDelta);
#ifdef XP_WIN
nsIntRect toScroll;
if (!updateRegion.IsEmpty()) {
nsRegion regionToScroll;
regionToScroll.Sub(nsRect(nsPoint(0,0), GetBounds().Size()),
updateRegion);
nsRegionRectIterator iter(regionToScroll);
nsRect biggestRect(0,0,0,0);
const nsRect* r;
for (r = iter.Next(); r; r = iter.Next()) {
if (PRInt64(r->width)*PRInt64(r->height) > PRInt64(biggestRect.width)*PRInt64(biggestRect.height)) {
biggestRect = *r;
}
// Compute the region that needs to be updated by the bit-blit scroll
nsRect bounds(nsPoint(0,0), GetBounds().Size());
nsRegion regionToScroll;
regionToScroll.Sub(bounds, updateRegion);
// Only the area corresponding to the widget bounds, translated
// by the scroll amount, will actually be filled by the blit
regionToScroll.And(regionToScroll, bounds - aTwipsDelta);
// Find the largest rectangle in that region
nsRegionRectIterator iter(regionToScroll);
nsRect biggestRect(0,0,0,0);
const nsRect* r;
for (r = iter.Next(); r; r = iter.Next()) {
if (PRInt64(r->width)*PRInt64(r->height) > PRInt64(biggestRect.width)*PRInt64(biggestRect.height)) {
biggestRect = *r;
}
toScrollPtr = &toScroll;
toScroll = biggestRect.ToInsidePixels(aP2A);
biggestRect = toScroll.ToAppUnits(aP2A);
regionToScroll.Sub(regionToScroll, biggestRect);
updateRegion.Or(updateRegion, regionToScroll);
}
#endif
// Convert the largest rectangle to widget device pixel coordinates
nsIntRect destScroll = (biggestRect + nearestWidgetOffset).ToInsidePixels(aP2A);
// Convert it back to view-relative appunits, since we shrank it in
// ToInsidePixels
biggestRect = destScroll.ToAppUnits(aP2A) - nearestWidgetOffset;
// Make sure we repaint the area we've decided not to bit-blit to
regionToScroll.Sub(regionToScroll, biggestRect);
updateRegion.Or(updateRegion, regionToScroll);
// Scroll the contents of the widget by the specified amount, and scroll
// the child widgets
scrollWidget->Scroll(aPixDelta.x, aPixDelta.y, toScrollPtr);
// Compute the area that's being exposed by the scroll operation
// and make sure it gets repainted
nsRegion exposedArea;
exposedArea.Sub(bounds, bounds - aTwipsDelta);
updateRegion.Or(updateRegion, exposedArea);
nearestWidget->Scroll(aPixDelta, destScroll - aPixDelta,
aConfigurations);
AdjustChildWidgets(aScrolledView, nearestWidgetOffset, aP2A, PR_TRUE);
mViewManager->UpdateViewAfterScroll(this, updateRegion);
}
}
@ -686,16 +669,15 @@ NS_IMETHODIMP nsScrollPortView::ScrollToImpl(nscoord aX, nscoord aY)
if (!scrolledView) return NS_ERROR_FAILURE;
// move the scrolled view to the new location
// Note that child widgets may be scrolled by the native widget scrolling,
// so don't update their positions
scrolledView->SetPositionIgnoringChildWidgets(-aX, -aY);
// notify the listeners.
nsTArray<nsIWidget::Configuration> configurations;
if (nsnull != mListeners) {
if (NS_SUCCEEDED(mListeners->Count(&listenerCount))) {
for (PRUint32 i = 0; i < listenerCount; i++) {
if (NS_SUCCEEDED(mListeners->QueryElementAt(i, kScrollPositionListenerIID, (void**)&listener))) {
listener->ViewPositionDidChange(this);
listener->ViewPositionDidChange(this, &configurations);
NS_RELEASE(listener);
}
}
@ -708,7 +690,7 @@ NS_IMETHODIMP nsScrollPortView::ScrollToImpl(nscoord aX, nscoord aY)
mOffsetX = aX;
mOffsetY = aY;
Scroll(scrolledView, twipsDelta, nsIntPoint(dxPx, dyPx), p2a);
Scroll(scrolledView, twipsDelta, nsIntPoint(dxPx, dyPx), p2a, configurations);
mViewManager->SynthesizeMouseMove(PR_TRUE);

View File

@ -60,7 +60,6 @@ public:
void** aInstancePtr);
//nsIScrollableView interface
NS_IMETHOD CreateScrollControls(nsNativeWidget aNative = nsnull);
NS_IMETHOD GetContainerSize(nscoord *aWidth, nscoord *aHeight) const;
NS_IMETHOD SetScrolledView(nsIView *aScrolledView);
NS_IMETHOD GetScrolledView(nsIView *&aScrolledView) const;
@ -107,7 +106,9 @@ protected:
virtual ~nsScrollPortView();
//private
void Scroll(nsView *aScrolledView, nsPoint aTwipsDelta, nsIntPoint aPixDelta, nscoord aP2A);
void Scroll(nsView *aScrolledView, nsPoint aTwipsDelta,
nsIntPoint aPixDelta, nscoord aP2A,
const nsTArray<nsIWidget::Configuration>& aConfigurations);
PRBool CannotBitBlt(nsView* aScrolledView);
nsresult CalcScrollOverflow(nscoord aX, nscoord aY, PRInt32& aOverflowX, PRInt32& aOverflowY);

View File

@ -654,7 +654,6 @@ nsViewManager::WillBitBlit(nsView* aView, nsPoint aScrollAmount)
NS_PRECONDITION(aView, "Must have a view");
NS_PRECONDITION(!aView->NeedsInvalidateFrameOnScroll(), "We shouldn't be BitBlting.");
NS_PRECONDITION(aView->HasWidget(), "View must have a widget");
++mScrollCnt;
@ -670,24 +669,10 @@ nsViewManager::UpdateViewAfterScroll(nsView *aView, const nsRegion& aUpdateRegio
{
NS_ASSERTION(RootViewManager()->mScrollCnt > 0,
"Someone forgot to call WillBitBlit()");
// Look at the view's clipped rect. It may be that part of the view is clipped out
// in which case we don't need to worry about invalidating the clipped-out part.
nsRect damageRect = aView->GetDimensions();
if (damageRect.IsEmpty()) {
// Don't forget to undo mScrollCnt!
--RootViewManager()->mScrollCnt;
return;
}
nsView* displayRoot = GetDisplayRootFor(aView);
nsPoint offset = aView->GetOffsetTo(displayRoot);
damageRect.MoveBy(offset);
UpdateWidgetArea(displayRoot, displayRoot->GetWidget(),
nsRegion(damageRect), aView);
if (!aUpdateRegion.IsEmpty()) {
// XXX We should update the region, not the bounds rect, but that requires
// a little more work. Fix this when we reshuffle this code.
nsView* displayRoot = GetDisplayRootFor(aView);
nsPoint offset = aView->GetOffsetTo(displayRoot);
nsRegion update(aUpdateRegion);
update.MoveBy(offset);
UpdateWidgetArea(displayRoot, displayRoot->GetWidget(),
@ -1572,11 +1557,7 @@ PRBool nsViewManager::CanScrollWithBitBlt(nsView* aView, nsPoint aDelta,
aUpdateRegion->MoveBy(-displayOffset);
#if defined(MOZ_WIDGET_GTK2) || defined(XP_OS2)
return aUpdateRegion->IsEmpty();
#else
return GetArea(aUpdateRegion->GetBounds()) < GetArea(parentBounds)/2;
#endif
}
NS_IMETHODIMP nsViewManager::SetViewFloating(nsIView *aView, PRBool aFloating)

View File

@ -42,6 +42,7 @@
#include "nsColor.h"
#include "nsCoord.h"
#include "nsRect.h"
#include "nsPoint.h"
#include "prthread.h"
#include "nsEvent.h"
@ -102,8 +103,8 @@ typedef nsEventStatus (* EVENT_CALLBACK)(nsGUIEvent *event);
#endif
#define NS_IWIDGET_IID \
{ 0x8f0869be, 0x6a53, 0x4f21, \
{ 0xa9, 0x64, 0x90, 0xd9, 0x26, 0x04, 0x05, 0xa3 } }
{ 0x9b8d70bd, 0x068e, 0x4465, \
{ 0x8a, 0xd2, 0x4c, 0xf7, 0x96, 0x67, 0xe4, 0xfc } }
/*
* Window shadow styles
@ -708,15 +709,25 @@ class nsIWidget : public nsISupports {
virtual nsIToolkit* GetToolkit() = 0;
/**
* Scroll this widget.
*
* @param aDx amount to scroll along the x-axis
* @param aDy amount to scroll along the y-axis.
* @param aClipRect clipping rectangle to limit the scroll to.
* Scroll a rectangle in this widget and (as simultaneously as
* possible) modify the specified child widgets.
*
* This will invalidate areas of the children that have changed, unless
* they have just moved by the scroll amount, but does not need to
* invalidate any part of this widget, except where the scroll
* operation fails to blit because part of the window is unavailable
* (e.g. partially offscreen).
*
* @param aDelta amount to scroll (device pixels)
* @param aSource rectangle to copy (device pixels relative to this
* widget)
* @param aReconfigureChildren commands to set the bounds and clip
* region of a subset of the children of this widget; these should
* be performed simultaneously with the scrolling, as far as possible,
* to avoid visual artifacts.
*/
NS_IMETHOD Scroll(PRInt32 aDx, PRInt32 aDy, nsIntRect *aClipRect) = 0;
virtual void Scroll(const nsIntPoint& aDelta, const nsIntRect& aSource,
const nsTArray<Configuration>& aReconfigureChildren) = 0;
/**
* Internal methods

View File

@ -350,7 +350,9 @@ public:
virtual void* GetNativeData(PRUint32 aDataType);
virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
NS_IMETHOD Scroll(PRInt32 aDx, PRInt32 aDy, nsIntRect *aClipRect);
virtual void Scroll(const nsIntPoint& aDelta,
const nsIntRect& aSource,
const nsTArray<Configuration>& aConfigurations);
virtual nsIntPoint WidgetToScreenOffset();
NS_IMETHOD BeginResizingChildren(void);
NS_IMETHOD EndResizingChildren(void);

View File

@ -1727,143 +1727,43 @@ nsresult nsChildView::ConfigureChildren(const nsTArray<Configuration>& aConfigur
return NS_OK;
}
// Scroll the bits of a view and its children
// FIXME: I'm sure the invalidating can be optimized, just no time now.
NS_IMETHODIMP nsChildView::Scroll(PRInt32 aDx, PRInt32 aDy, nsIntRect *aClipRect)
void nsChildView::Scroll(const nsIntPoint& aDelta, const nsIntRect& aSource,
const nsTArray<Configuration>& aConfigurations)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
NS_ASSERTION(mParentView, "Attempting to scroll a view that does not have a parent");
if (!mParentView)
return NS_ERROR_NOT_AVAILABLE;
return;
BOOL viewWasDirty = NO;
if (mVisible) {
viewWasDirty = [mView needsDisplay];
NSSize scrollVector = {aDx,aDy};
[mView scrollRect: [mView visibleRect] by:scrollVector];
NSRect rect;
GeckoRectToNSRect(aSource, rect);
NSSize scrollVector = {aDelta.x, aDelta.y};
[mView scrollRect:rect by:scrollVector];
}
// Scroll the children (even if the widget is not visible)
for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
// We use resize rather than move since it gives us control
// over repainting. We can scroll like a bat out of hell
// by not wasting time invalidating the widgets, since it's
// completely unnecessary to do so.
nsIntRect bounds;
kid->GetBounds(bounds);
kid->Resize(bounds.x + aDx, bounds.y + aDy, bounds.width, bounds.height, PR_FALSE);
// Don't force invalidation of the child if it's moving by the scroll
// amount and not changing size
for (PRUint32 i = 0; i < aConfigurations.Length(); ++i) {
const Configuration& configuration = aConfigurations[i];
nsIntRect oldBounds;
configuration.mChild->GetBounds(oldBounds);
ApplyConfiguration(this, aConfigurations[i],
oldBounds + aDelta != configuration.mBounds);
}
if (mOnDestroyCalled)
return NS_OK;
return;
if (mVisible) {
if (viewWasDirty) {
[mView setNeedsDisplay:YES];
}
else {
NSRect frame = [mView visibleRect];
NSRect horizInvalid = frame;
NSRect vertInvalid = frame;
if (aDx != 0) {
horizInvalid.size.width = abs(aDx);
if (aDx < 0)
horizInvalid.origin.x = frame.origin.x + frame.size.width + aDx;
[mView setNeedsDisplayInRect: horizInvalid];
}
if (aDy != 0) {
vertInvalid.size.height = abs(aDy);
if (aDy < 0)
vertInvalid.origin.y = frame.origin.y + frame.size.height + aDy;
[mView setNeedsDisplayInRect: vertInvalid];
}
// We also need to check for any ChildViews which overlap this widget
// but are not descendent widgets. If there are any, we need to
// invalidate the area of this view that these ChildViews will have been
// blitted into, since these widgets aren't supposed to scroll with
// this widget.
// To do this, start at the root Gecko NSView, and walk down along
// our ancestor view chain, looking at all the subviews in each level
// of the hierarchy. If we find a non-ancestor view that overlaps
// this view, invalidate the area around it.
// We need to convert all rects to a common ancestor view to intersect
// them, since a view's frame is in the coordinate space of its parent.
// Use mParentView as the frame of reference.
NSRect selfFrame = [mParentView convertRect:[mView frame] fromView:[mView superview]];
NSView* view = mParentView;
BOOL selfLevel = NO;
while (!selfLevel) {
NSView* nextAncestorView = nil;
NSArray* subviews = [view subviews];
for (unsigned int i = 0; i < [subviews count]; ++i) {
NSView* subView = [subviews objectAtIndex: i];
if (subView == mView)
selfLevel = YES;
else if ([mView isDescendantOf:subView])
nextAncestorView = subView;
else {
NSRect intersectArea = NSIntersectionRect([mParentView convertRect:[subView frame] fromView:[subView superview]], selfFrame);
if (!NSIsEmptyRect(intersectArea)) {
NSPoint origin = [mView convertPoint:intersectArea.origin fromView:mParentView];
if (aDy != 0) {
vertInvalid.origin.x = origin.x;
if (aDy < 0) // scrolled down, invalidate above
vertInvalid.origin.y = origin.y + aDy;
else // invalidate below
vertInvalid.origin.y = origin.y + intersectArea.size.height;
vertInvalid.size.width = intersectArea.size.width;
[mView setNeedsDisplayInRect: vertInvalid];
}
if (aDx != 0) {
horizInvalid.origin.y = origin.y;
if (aDx < 0) // scrolled right, invalidate to the left
horizInvalid.origin.x = origin.x + aDx;
else // invalidate to the right
horizInvalid.origin.x = origin.x + intersectArea.size.width;
horizInvalid.size.height = intersectArea.size.height;
[mView setNeedsDisplayInRect: horizInvalid];
}
}
}
}
view = nextAncestorView;
}
}
[mView setNeedsDisplay:viewWasDirty];
}
// This is an evil hack that doesn't always work.
//
// Drawing plugins in a Cocoa environment is tricky, because the
// plugins are living in a Carbon WindowRef/BeginUpdate/EndUpdate
// world, and Cocoa has its own notion of dirty rectangles. Throw
// Quartz Extreme and QuickTime into the mix, and things get bad.
//
// This code is working around a cosmetic issue seen when Quartz Extreme
// is active, and you're scrolling a page with a QuickTime plugin; areas
// outside the plugin fail to scroll properly. This [display] ensures that
// the view is properly drawn before the next Scroll call.
//
// The time this doesn't work is when you're scrolling a page containing
// an iframe which in turn contains a plugin.
//
// This is turned off because it makes scrolling pages with plugins slow.
//
//if ([mView childViewHasPlugin])
// [mView display];
return NS_OK;
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
NS_OBJC_END_TRY_ABORT_BLOCK;
}
// Invokes callback and ProcessEvent methods on Event Listener object

View File

@ -227,7 +227,8 @@ public:
NS_IMETHOD Invalidate(PRBool aIsSynchronous);
NS_IMETHOD Update();
virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
NS_IMETHOD Scroll(PRInt32 aDx, PRInt32 aDy, nsIntRect *alCipRect) { return NS_OK; }
virtual void Scroll(const nsIntPoint& aDelta, const nsIntRect& aSource,
const nsTArray<Configuration>& aConfigurations);
NS_IMETHOD BeginResizingChildren(void) { return NS_OK; }
NS_IMETHOD EndResizingChildren(void) { return NS_OK; }
NS_IMETHOD DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus) ;

View File

@ -813,11 +813,20 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
nsresult
nsCocoaWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
{
for (PRUint32 i = 0; i < aConfigurations.Length(); ++i) {
nsChildView::ApplyConfiguration(this, aConfigurations[i], PR_TRUE);
if (mPopupContentView) {
mPopupContentView->ConfigureChildren(aConfigurations);
}
return NS_OK;
}
}
void
nsCocoaWindow::Scroll(const nsIntPoint& aDelta, const nsIntRect& aSource,
const nsTArray<Configuration>& aConfigurations)
{
if (mPopupContentView) {
mPopupContentView->Scroll(aDelta, aSource, aConfigurations);
}
}
void nsCocoaWindow::MakeBackgroundTransparent(PRBool aTransparent)
{

View File

@ -1731,35 +1731,46 @@ nsWindow::Update()
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Scroll(PRInt32 aDx,
PRInt32 aDy,
nsIntRect *aClipRect)
void
nsWindow::Scroll(const nsIntPoint& aDelta, const nsIntRect& aSource,
const nsTArray<Configuration>& aConfigurations)
{
if (!mDrawingarea)
return NS_OK;
D_DEBUG_AT( ns_Window, "%s( %4d,%4d )\n", __FUNCTION__, aDx, aDy );
if (aClipRect) {
D_DEBUG_AT( ns_Window, " -> aClipRect: %4d,%4d-%4dx%4d\n",
aClipRect->x, aClipRect->y, aClipRect->width, aClipRect->height );
if (!mDrawingarea) {
NS_ERROR("Cannot scroll widget");
return;
}
moz_drawingarea_scroll(mDrawingarea, aDx, aDy);
// Update bounds on our child windows
for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
nsIntRect bounds;
kid->GetBounds(bounds);
bounds.x += aDx;
bounds.y += aDy;
static_cast<nsBaseWidget*>(kid)->SetBounds(bounds);
nsAutoTArray<nsWindow*,1> windowsToShow;
// Hide any widgets that are becoming invisible or that are moving.
// Moving widgets are hidden for the duration of the scroll so that
// the XCopyArea treats their drawn pixels as part of the window
// that should be scrolled. This works well when the widgets are
// moving because they're being scrolled, which is normally true.
for (PRUint32 i = 0; i < aConfigurations.Length(); ++i) {
const Configuration& configuration = aConfigurations[i];
nsWindow* w = static_cast<nsWindow*>(configuration.mChild);
NS_ASSERTION(w->GetParent() == this,
"Configured widget is not a child");
if (w->mIsShown &&
(configuration.mClipRegion.IsEmpty() ||
configuration.mBounds != w->mBounds)) {
w->NativeShow(PR_FALSE);
windowsToShow.AppendElement(w);
}
}
// Process all updates so that everything is drawn.
gdk_window_process_all_updates();
return NS_OK;
GdkRectangle gdkSource =
{ aSource.x, aSource.y, aSource.width, aSource.height };
GdkRegion* region = gdk_region_rectangle(&gdkSource);
gdk_window_move_region(GDK_WINDOW(mDrawingarea->inner_window),
region, aDelta.x, aDelta.y);
gdk_region_destroy(region);
ConfigureChildren(aConfigurations);
for (PRUint32 i = 0; i < windowsToShow.Length(); ++i) {
windowsToShow[i]->NativeShow(PR_TRUE);
}
}
void*

View File

@ -187,9 +187,8 @@ public:
NS_IMETHOD Invalidate(const nsIntRect &aRect,
PRBool aIsSynchronous);
NS_IMETHOD Update();
NS_IMETHOD Scroll(PRInt32 aDx,
PRInt32 aDy,
nsIntRect *aClipRect);
virtual void Scroll(const nsIntPoint& aDelta, const nsIntRect& aSource,
const nsTArray<Configuration>& aReconfigureChildren);
virtual void* GetNativeData(PRUint32 aDataType);
NS_IMETHOD SetBorderStyle(nsBorderStyle aBorderStyle);
NS_IMETHOD SetTitle(const nsAString& aTitle);

View File

@ -147,6 +147,8 @@
#include "nsAppDirectoryServiceDefs.h"
#include "nsXPIDLString.h"
#include "nsWidgetsCID.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#if defined(WINCE)
#include "nsWindowCE.h"
@ -2297,43 +2299,54 @@ NS_IMETHODIMP nsWindow::Update()
*
**************************************************************/
// Invalidates a window if it's not one of ours, for example
// a window created by a plugin.
BOOL CALLBACK nsWindow::InvalidateForeignChildWindows(HWND aWnd, LPARAM aMsg)
void
nsWindow::Scroll(const nsIntPoint& aDelta, const nsIntRect& aSource,
const nsTArray<Configuration>& aConfigurations)
{
LONG_PTR proc = ::GetWindowLongPtrW(aWnd, GWLP_WNDPROC);
if (proc != (LONG_PTR)&nsWindow::WindowProc) {
// This window is not one of our windows so invalidate it.
VERIFY(::InvalidateRect(aWnd, NULL, FALSE));
}
return TRUE;
}
// Scroll
NS_METHOD nsWindow::Scroll(PRInt32 aDx, PRInt32 aDy, nsIntRect *aClipRect)
{
RECT trect;
if (nsnull != aClipRect)
{
trect.left = aClipRect->x;
trect.top = aClipRect->y;
trect.right = aClipRect->XMost();
trect.bottom = aClipRect->YMost();
// We can use SW_SCROLLCHILDREN if all the windows that intersect the
// affected area are moving by the scroll amount.
// First, build the set of widgets that are to be moved by the scroll
// amount.
// At the same time, set the clip region of all changed windows to the
// intersection of the current and new regions.
nsTHashtable<nsPtrHashKey<nsWindow> > scrolledWidgets;
scrolledWidgets.Init();
for (PRUint32 i = 0; i < aConfigurations.Length(); ++i) {
const Configuration& configuration = aConfigurations[i];
nsWindow* w = static_cast<nsWindow*>(configuration.mChild);
NS_ASSERTION(w->GetParent() == this,
"Configured widget is not a child");
if (configuration.mBounds == w->mBounds + aDelta) {
scrolledWidgets.PutEntry(w);
}
w->SetWindowClipRegion(configuration.mClipRegion, PR_TRUE);
}
::ScrollWindowEx(mWnd, aDx, aDy, NULL, (nsnull != aClipRect) ? &trect : NULL,
NULL, NULL, SW_INVALIDATE | SW_SCROLLCHILDREN);
// Invalidate all child windows that aren't ours; we're moving them, and we
// expect them to be painted at the new location even if they're outside the
// region we're bit-blit scrolling. See bug 387701.
#if !defined(WINCE)
::EnumChildWindows(GetWindowHandle(), nsWindow::InvalidateForeignChildWindows, NULL);
#else
nsWindowCE::EnumChildWindows(GetWindowHandle(), nsWindow::InvalidateForeignChildWindows, NULL);
#endif
::UpdateWindow(mWnd);
return NS_OK;
// Now check if any of our children would be affected by
// SW_SCROLLCHILDREN but not supposed to scroll.
nsIntRect affectedRect;
affectedRect.UnionRect(aSource, aSource + aDelta);
// We pass SW_INVALIDATE because areas that get scrolled into view
// from offscreen (but inside the scroll area) need to be repainted.
UINT flags = SW_SCROLLCHILDREN | SW_INVALIDATE;
for (nsWindow* w = static_cast<nsWindow*>(GetFirstChild()); w;
w = static_cast<nsWindow*>(w->GetNextSibling())) {
if (w->mBounds.Intersects(affectedRect) &&
!scrolledWidgets.GetEntry(w)) {
flags &= ~SW_SCROLLCHILDREN;
break;
}
}
nsIntRect destRect = aSource + aDelta;
RECT clip = { affectedRect.x, affectedRect.y, affectedRect.XMost(), affectedRect.YMost() };
::ScrollWindowEx(mWnd, aDelta.x, aDelta.y, &clip, &clip, NULL, NULL, flags);
// Now make sure all children actually get positioned, sized and clipped
// correctly. If SW_SCROLLCHILDREN already moved widgets to their correct
// locations, then the SetWindowPos calls this triggers will just be
// no-ops.
ConfigureChildren(aConfigurations);
}
/**************************************************************

View File

@ -147,7 +147,8 @@ public:
NS_IMETHOD Invalidate(PRBool aIsSynchronous);
NS_IMETHOD Invalidate(const nsIntRect & aRect, PRBool aIsSynchronous);
NS_IMETHOD Update();
NS_IMETHOD Scroll(PRInt32 aDx, PRInt32 aDy, nsIntRect *aClipRect);
virtual void Scroll(const nsIntPoint& aDelta, const nsIntRect& aSource,
const nsTArray<Configuration>& aReconfigureChildren);
virtual void* GetNativeData(PRUint32 aDataType);
virtual void FreeNativeData(void * data, PRUint32 aDataType);
NS_IMETHOD SetTitle(const nsAString& aTitle);
@ -246,7 +247,6 @@ protected:
static BOOL CALLBACK BroadcastMsgToChildren(HWND aWnd, LPARAM aMsg);
static BOOL CALLBACK BroadcastMsg(HWND aTopWindow, LPARAM aMsg);
static BOOL CALLBACK DispatchStarvedPaints(HWND aTopWindow, LPARAM aMsg);
static BOOL CALLBACK InvalidateForeignChildWindows(HWND aWnd, LPARAM aMsg);
static LRESULT CALLBACK MozSpecialMsgFilter(int code, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK MozSpecialWndProc(int code, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK MozSpecialMouseProc(int code, WPARAM wParam, LPARAM lParam);