Bug 174049. Smooth scrolling. Original patch by Neil cronin. r+sr=kin

This commit is contained in:
roc+%cs.cmu.edu 2003-03-24 05:29:45 +00:00
parent 4f81dc78d1
commit 64131124e6
9 changed files with 422 additions and 133 deletions

View File

@ -167,6 +167,7 @@ XUL_ATOM(tabpanel, "tabpanel")
XUL_ATOM(index, "index")
XUL_ATOM(maxpos, "maxpos")
XUL_ATOM(curpos, "curpos")
XUL_ATOM(smooth, "smooth")
XUL_ATOM(scrollbarbutton, "scrollbarbutton")
XUL_ATOM(increment, "increment")
XUL_ATOM(pageincrement, "pageincrement")

View File

@ -172,7 +172,7 @@ public:
nsIScrollableView* GetScrollableView(nsIPresContext* aPresContext);
void ScrollbarChanged(nsIPresContext* aPresContext, nscoord aX, nscoord aY);
void ScrollbarChanged(nsIPresContext* aPresContext, nscoord aX, nscoord aY, PRUint32 aFlags);
void SetScrollbarVisibility(nsIBox* aScrollbar, PRBool aVisible);
@ -199,6 +199,7 @@ public:
PRPackedBool mFirstPass;
PRPackedBool mIsRoot;
PRPackedBool mNeverReflowed;
PRPackedBool mScrollingInitiated;
};
NS_IMPL_ISUPPORTS2(nsGfxScrollFrameInner, nsIDocumentObserver, nsIScrollPositionListener)
@ -226,6 +227,7 @@ nsGfxScrollFrame::nsGfxScrollFrame(nsIPresShell* aShell, nsIDocument* aDocument,
mPresContext = nsnull;
mInner->mIsRoot = PR_FALSE;
mInner->mNeverReflowed = PR_TRUE;
mInner->mScrollingInitiated = PR_FALSE;
SetLayoutManager(nsnull);
}
@ -936,6 +938,11 @@ nsGfxScrollFrameInner::AttributeChanged(nsIDocument *aDocument,
PRInt32 aModType,
nsChangeHint aHint)
{
// Don't reenter if we're getting this attribute change notification
// because we caused the view to scroll and the view is telling
// us about it.
if (mScrollingInitiated) return NS_OK;
if (mHScrollbarBox && mVScrollbarBox)
{
nsIFrame* hframe = nsnull;
@ -989,10 +996,24 @@ nsGfxScrollFrameInner::AttributeChanged(nsIDocument *aDocument,
s->GetScrollPosition(curPosX, curPosY);
if ((x*mOnePixel) == curPosX && (y*mOnePixel) == curPosY)
return NS_OK;
PRBool isSmooth = vcontent->HasAttr(kNameSpaceID_None, nsXULAtoms::smooth)
|| hcontent->HasAttr(kNameSpaceID_None, nsXULAtoms::smooth);
s->RemoveScrollPositionListener(this);
ScrollbarChanged(mOuter->mPresContext, x*mOnePixel, y*mOnePixel);
s->AddScrollPositionListener(this);
// Remember that we asked the view to scroll, so we don't need to
// take notice of any attribute change caused by the view's scrolling
// action.
mScrollingInitiated = PR_TRUE;
ScrollbarChanged(mOuter->mPresContext, x*mOnePixel, y*mOnePixel, isSmooth ? NS_VMREFRESH_SMOOTHSCROLL : 0);
if (isSmooth) {
// Make sure an attribute-setting callback occurs even if the view didn't actually move yet
// We need to make sure other listeners see that the scroll position is not (yet)
// what they thought it was.
s->GetScrollPosition(curPosX, curPosY);
ScrollPositionDidChange(s, curPosX, curPosY);
}
mScrollingInitiated = PR_FALSE;
// Fire the onScroll event now that we have scrolled
nsCOMPtr<nsIPresShell> presShell;
mOuter->mPresContext->GetShell(getter_AddRefs(presShell));
@ -1013,7 +1034,6 @@ nsGfxScrollFrameInner::AttributeChanged(nsIDocument *aDocument,
return NS_OK;
}
nsIScrollableView*
nsGfxScrollFrameInner::GetScrollableView(nsIPresContext* aPresContext)
{
@ -1476,7 +1496,6 @@ nsGfxScrollFrameInner::Layout(nsBoxLayoutState& aState)
SetAttribute(mVScrollbarBox, nsXULAtoms::curpos, 0);
}
mVScrollbarBox->GetPrefSize(aState, vSize);
}
@ -1556,10 +1575,10 @@ nsGfxScrollFrameInner::Layout(nsBoxLayoutState& aState)
}
void
nsGfxScrollFrameInner::ScrollbarChanged(nsIPresContext* aPresContext, nscoord aX, nscoord aY)
nsGfxScrollFrameInner::ScrollbarChanged(nsIPresContext* aPresContext, nscoord aX, nscoord aY, PRUint32 aFlags)
{
nsIScrollableView* scrollable = GetScrollableView(aPresContext);
scrollable->ScrollTo(aX,aY, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
scrollable->ScrollTo(aX, aY, aFlags);
// printf("scrolling to: %d, %d\n", aX, aY);
}
@ -1648,17 +1667,18 @@ nsGfxScrollFrameInner::SetScrollbarVisibility(nsIBox* aScrollbar, PRBool aVisibl
PRBool old = PR_TRUE;
nsAutoString value;
if (NS_CONTENT_ATTR_HAS_VALUE == content->GetAttr(kNameSpaceID_None, nsXULAtoms::collapsed, value))
old = PR_FALSE;
if (aVisible == old)
return;
if (!aVisible)
if (!aVisible) {
content->SetAttr(kNameSpaceID_None, nsXULAtoms::collapsed, NS_LITERAL_STRING("true"), PR_TRUE);
else
} else {
content->UnsetAttr(kNameSpaceID_None, nsXULAtoms::collapsed, PR_TRUE);
}
nsCOMPtr<nsIScrollbarFrame> scrollbar(do_QueryInterface(aScrollbar));
if (scrollbar) {

View File

@ -172,7 +172,7 @@ public:
nsIScrollableView* GetScrollableView(nsIPresContext* aPresContext);
void ScrollbarChanged(nsIPresContext* aPresContext, nscoord aX, nscoord aY);
void ScrollbarChanged(nsIPresContext* aPresContext, nscoord aX, nscoord aY, PRUint32 aFlags);
void SetScrollbarVisibility(nsIBox* aScrollbar, PRBool aVisible);
@ -199,6 +199,7 @@ public:
PRPackedBool mFirstPass;
PRPackedBool mIsRoot;
PRPackedBool mNeverReflowed;
PRPackedBool mScrollingInitiated;
};
NS_IMPL_ISUPPORTS2(nsGfxScrollFrameInner, nsIDocumentObserver, nsIScrollPositionListener)
@ -226,6 +227,7 @@ nsGfxScrollFrame::nsGfxScrollFrame(nsIPresShell* aShell, nsIDocument* aDocument,
mPresContext = nsnull;
mInner->mIsRoot = PR_FALSE;
mInner->mNeverReflowed = PR_TRUE;
mInner->mScrollingInitiated = PR_FALSE;
SetLayoutManager(nsnull);
}
@ -936,6 +938,11 @@ nsGfxScrollFrameInner::AttributeChanged(nsIDocument *aDocument,
PRInt32 aModType,
nsChangeHint aHint)
{
// Don't reenter if we're getting this attribute change notification
// because we caused the view to scroll and the view is telling
// us about it.
if (mScrollingInitiated) return NS_OK;
if (mHScrollbarBox && mVScrollbarBox)
{
nsIFrame* hframe = nsnull;
@ -989,10 +996,24 @@ nsGfxScrollFrameInner::AttributeChanged(nsIDocument *aDocument,
s->GetScrollPosition(curPosX, curPosY);
if ((x*mOnePixel) == curPosX && (y*mOnePixel) == curPosY)
return NS_OK;
PRBool isSmooth = vcontent->HasAttr(kNameSpaceID_None, nsXULAtoms::smooth)
|| hcontent->HasAttr(kNameSpaceID_None, nsXULAtoms::smooth);
s->RemoveScrollPositionListener(this);
ScrollbarChanged(mOuter->mPresContext, x*mOnePixel, y*mOnePixel);
s->AddScrollPositionListener(this);
// Remember that we asked the view to scroll, so we don't need to
// take notice of any attribute change caused by the view's scrolling
// action.
mScrollingInitiated = PR_TRUE;
ScrollbarChanged(mOuter->mPresContext, x*mOnePixel, y*mOnePixel, isSmooth ? NS_VMREFRESH_SMOOTHSCROLL : 0);
if (isSmooth) {
// Make sure an attribute-setting callback occurs even if the view didn't actually move yet
// We need to make sure other listeners see that the scroll position is not (yet)
// what they thought it was.
s->GetScrollPosition(curPosX, curPosY);
ScrollPositionDidChange(s, curPosX, curPosY);
}
mScrollingInitiated = PR_FALSE;
// Fire the onScroll event now that we have scrolled
nsCOMPtr<nsIPresShell> presShell;
mOuter->mPresContext->GetShell(getter_AddRefs(presShell));
@ -1013,7 +1034,6 @@ nsGfxScrollFrameInner::AttributeChanged(nsIDocument *aDocument,
return NS_OK;
}
nsIScrollableView*
nsGfxScrollFrameInner::GetScrollableView(nsIPresContext* aPresContext)
{
@ -1476,7 +1496,6 @@ nsGfxScrollFrameInner::Layout(nsBoxLayoutState& aState)
SetAttribute(mVScrollbarBox, nsXULAtoms::curpos, 0);
}
mVScrollbarBox->GetPrefSize(aState, vSize);
}
@ -1556,10 +1575,10 @@ nsGfxScrollFrameInner::Layout(nsBoxLayoutState& aState)
}
void
nsGfxScrollFrameInner::ScrollbarChanged(nsIPresContext* aPresContext, nscoord aX, nscoord aY)
nsGfxScrollFrameInner::ScrollbarChanged(nsIPresContext* aPresContext, nscoord aX, nscoord aY, PRUint32 aFlags)
{
nsIScrollableView* scrollable = GetScrollableView(aPresContext);
scrollable->ScrollTo(aX,aY, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
scrollable->ScrollTo(aX, aY, aFlags);
// printf("scrolling to: %d, %d\n", aX, aY);
}
@ -1648,17 +1667,18 @@ nsGfxScrollFrameInner::SetScrollbarVisibility(nsIBox* aScrollbar, PRBool aVisibl
PRBool old = PR_TRUE;
nsAutoString value;
if (NS_CONTENT_ATTR_HAS_VALUE == content->GetAttr(kNameSpaceID_None, nsXULAtoms::collapsed, value))
old = PR_FALSE;
if (aVisible == old)
return;
if (!aVisible)
if (!aVisible) {
content->SetAttr(kNameSpaceID_None, nsXULAtoms::collapsed, NS_LITERAL_STRING("true"), PR_TRUE);
else
} else {
content->UnsetAttr(kNameSpaceID_None, nsXULAtoms::collapsed, PR_TRUE);
}
nsCOMPtr<nsIScrollbarFrame> scrollbar(do_QueryInterface(aScrollbar));
if (scrollbar) {

View File

@ -205,9 +205,10 @@ nsScrollbarButtonFrame::MouseClicked()
nsAutoString curposStr;
curposStr.AppendInt(curpos);
content->SetAttr(kNameSpaceID_None, nsXULAtoms::smooth, NS_LITERAL_STRING("true"), PR_FALSE);
content->SetAttr(kNameSpaceID_None, nsXULAtoms::curpos, curposStr, PR_TRUE);
}
content->UnsetAttr(kNameSpaceID_None, nsXULAtoms::smooth, PR_FALSE);
}
}
nsresult

View File

@ -608,7 +608,7 @@ nsSliderFrame::HandleEvent(nsIPresContext* aPresContext,
if (isMouseOutsideThumb)
{
// XXX see bug 81586
SetCurrentPosition(scrollbar, thumbFrame, (int) (mThumbStart / onePixel / mRatio));
SetCurrentPosition(scrollbar, thumbFrame, (int) (mThumbStart / onePixel / mRatio), PR_FALSE);
return NS_OK;
}
@ -620,7 +620,7 @@ nsSliderFrame::HandleEvent(nsIPresContext* aPresContext,
pospx = nscoord(pospx/mRatio);
// set it
SetCurrentPosition(scrollbar, thumbFrame, pospx);
SetCurrentPosition(scrollbar, thumbFrame, pospx, PR_FALSE);
}
break;
@ -666,7 +666,7 @@ nsSliderFrame::HandleEvent(nsIPresContext* aPresContext,
pospx = nscoord(pospx/mRatio);
// set it
SetCurrentPosition(scrollbar, thumbFrame, pospx);
SetCurrentPosition(scrollbar, thumbFrame, pospx, PR_FALSE);
// hack to start dragging
@ -762,7 +762,7 @@ nsSliderFrame::PageUpDown(nsIFrame* aThumbFrame, nscoord change)
nscoord pageIncrement = GetPageIncrement(scrollbar);
PRInt32 curpos = GetCurrentPosition(scrollbar);
SetCurrentPosition(scrollbar, aThumbFrame, curpos + change*pageIncrement);
SetCurrentPosition(scrollbar, aThumbFrame, curpos + change*pageIncrement, PR_TRUE);
}
// called when the current position changed and we need to update the thumb's location
@ -836,8 +836,21 @@ nsSliderFrame::CurrentPositionChanged(nsIPresContext* aPresContext)
return NS_OK;
}
static void UpdateAttribute(nsIContent* aScrollbar, nscoord aNewPos, PRBool aNotify, PRBool aIsSmooth) {
nsAutoString str;
str.AppendInt(aNewPos);
if (aIsSmooth) {
aScrollbar->SetAttr(kNameSpaceID_None, nsXULAtoms::smooth, NS_LITERAL_STRING("true"), PR_FALSE);
}
aScrollbar->SetAttr(kNameSpaceID_None, nsXULAtoms::curpos, str, aNotify);
if (aIsSmooth) {
aScrollbar->UnsetAttr(kNameSpaceID_None, nsXULAtoms::smooth, PR_FALSE);
}
}
void
nsSliderFrame::SetCurrentPosition(nsIContent* scrollbar, nsIFrame* aThumbFrame, nscoord newpos)
nsSliderFrame::SetCurrentPosition(nsIContent* scrollbar, nsIFrame* aThumbFrame, nscoord newpos, PRBool aIsSmooth)
{
// get our current position and max position from our content node
@ -859,19 +872,13 @@ nsSliderFrame::SetCurrentPosition(nsIContent* scrollbar, nsIFrame* aThumbFrame,
scrollbarFrame->GetScrollbarMediator(getter_AddRefs(mediator));
if (mediator) {
mediator->PositionChanged(GetCurrentPosition(scrollbar), newpos);
nsAutoString newposStr;
newposStr.AppendInt(newpos);
scrollbar->SetAttr(kNameSpaceID_None, nsXULAtoms::curpos, newposStr, PR_FALSE);
UpdateAttribute(scrollbar, newpos, PR_FALSE, aIsSmooth);
CurrentPositionChanged(mPresContext);
return;
}
}
nsAutoString newposStr;
newposStr.AppendInt(newpos);
// set the new position
scrollbar->SetAttr(kNameSpaceID_None, nsXULAtoms::curpos, newposStr, PR_TRUE);
UpdateAttribute(scrollbar, newpos, PR_TRUE, aIsSmooth);
#ifdef DEBUG_SLIDER
printf("Current Pos=%d\n",newpos);
@ -1020,7 +1027,7 @@ nsSliderFrame::MouseDown(nsIDOMEvent* aMouseEvent)
GetContentOf(scrollbarBox, getter_AddRefs(scrollbar));
// set it
SetCurrentPosition(scrollbar, thumbFrame, pospx);
SetCurrentPosition(scrollbar, thumbFrame, pospx, PR_FALSE);
}
RemoveListener();

View File

@ -235,7 +235,7 @@ private:
void GetContentOf(nsIBox* aBox, nsIContent** aContent);
void PageUpDown(nsIFrame* aThumbFrame, nscoord change);
void SetCurrentPosition(nsIContent* scrollbar, nsIFrame* aThumbFrame, nscoord pos);
void SetCurrentPosition(nsIContent* scrollbar, nsIFrame* aThumbFrame, nscoord pos, PRBool aIsSmooth);
NS_IMETHOD DragThumb(nsIPresContext* aPresContext, PRBool aGrabMouseEvents);
void AddListener();
void RemoveListener();

View File

@ -551,5 +551,7 @@ public:
#define NS_VMREFRESH_IMMEDIATE 0x0002
//prevent "sync painting"
#define NS_VMREFRESH_NO_SYNC 0x0004
//animate scroll operation
#define NS_VMREFRESH_SMOOTHSCROLL 0x0008
#endif // nsIViewManager_h___

View File

@ -19,7 +19,7 @@
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Contributor(s): Neil Cronin (neil@rackle.com)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -51,11 +51,20 @@
#include "nsIScrollPositionListener.h"
#include "nsIRegion.h"
#include "nsViewManager.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsCOMPtr.h"
#include "nsIServiceManagerUtils.h"
#include <math.h>
static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID);
static NS_DEFINE_IID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
static NS_DEFINE_IID(kIClipViewIID, NS_ICLIPVIEW_IID);
#define SMOOTH_SCROLL_MSECS_PER_FRAME 10
#define SMOOTH_SCROLL_FRAMES 10
#define SMOOTH_SCROLL_PREF_NAME "general.smoothScroll"
nsScrollPortView::nsScrollPortView()
{
@ -63,6 +72,7 @@ nsScrollPortView::nsScrollPortView()
mOffsetXpx = mOffsetYpx = 0;
mListeners = nsnull;
mSmoothScroll = nsnull;
}
nsScrollPortView::~nsScrollPortView()
@ -79,6 +89,8 @@ nsScrollPortView::~nsScrollPortView()
mViewManager->SetRootScrollableView(nsnull);
}
}
delete mSmoothScroll;
}
nsresult nsScrollPortView::QueryInterface(const nsIID& aIID, void** aInstancePtr)
@ -208,117 +220,182 @@ NS_IMETHODIMP nsScrollPortView::GetScrollPreference(nsScrollPreference &aScrollP
return nsScrollPreference_kNeverScroll;
}
NS_IMETHODIMP nsScrollPortView::ScrollTo(nscoord aX, nscoord aY, PRUint32 aUpdateFlags)
{
// do nothing if the we aren't scrolling.
if (aX == mOffsetX && aY == mOffsetY)
return NS_OK;
static void ComputeVelocities(PRInt32 aCurVelocity, nscoord aCurPos, nscoord aDstPos,
PRInt32* aVelocities) {
PRInt32 absCurVelocity;
PRInt32 direction;
nsSize scrolledSize;
PRInt32 dxPx = 0, dyPx = 0;
// convert to pixels
nsIDeviceContext *dev;
float t2p;
float p2t;
mViewManager->GetDeviceContext(dev);
dev->GetAppUnitsToDevUnits(t2p);
dev->GetDevUnitsToAppUnits(p2t);
if (aCurPos < aDstPos) {
direction = 1;
if (aCurVelocity < 0) {
absCurVelocity = 0;
} else {
absCurVelocity = aCurVelocity;
}
} else {
direction = -1;
if (aCurVelocity > 0) {
absCurVelocity = 0;
} else {
absCurVelocity = -aCurVelocity;
}
}
NS_RELEASE(dev);
PRInt32 absDistance = direction*(aDstPos - aCurPos);
// The target velocity is the velocity we want to reach at frame N/2. We choose the
// target velocity so that after coming to a stop at frame N, we will have reached
// the scroll destination (actually we'll overshoot because of rounding, but that's
// the idea).
// It's chosen to satisfy
// aDstPos - aCurPos = N/2 (aTargetVelocity/2) + N/2 ((aCurVelocity + aTargetVelocity)/2)
// distance travelled avg. velocity in 1st half avg. velocity in 2nd half
PRInt32 absTargetVelocity = (PRInt32)ceil(2.0*absDistance/SMOOTH_SCROLL_FRAMES - 0.5*absCurVelocity);
// Update the scrolled view's position
// if we're going too fast already, slow down to the target velocity
if (absCurVelocity > absTargetVelocity) {
absCurVelocity = absTargetVelocity;
}
PRInt32 i;
const int halfFrames = SMOOTH_SCROLL_FRAMES/2;
for (i = 0; i < halfFrames; i++) {
aVelocities[i*2] = PR_MAX(0, absCurVelocity + (absTargetVelocity*i + halfFrames - 1)/halfFrames);
}
for (i = halfFrames; i < halfFrames*2; i++) {
aVelocities[i*2] = PR_MAX(0, (absTargetVelocity*(halfFrames*2 - (i - 1)) + halfFrames - 1)/halfFrames);
}
PRInt32 total = 0;
for (i = 0; i < SMOOTH_SCROLL_FRAMES; i++) {
total += aVelocities[i*2];
}
PRInt32 leftover = total - absDistance;
NS_ASSERTION(leftover >= 0, "Lost some distance due to rounding? We should have been rounding up only");
while (leftover > 0) {
for (i = SMOOTH_SCROLL_FRAMES - 1; i >= 0; i--) {
if (aVelocities[i*2] > 0) {
aVelocities[i*2]--;
leftover--;
if (leftover <= 0) {
break;
}
}
}
}
for (i = 0; i < SMOOTH_SCROLL_FRAMES; i++) {
aVelocities[i*2] *= direction;
}
}
static nsresult ClampScrollValues(nscoord& aX, nscoord& aY, nsScrollPortView* aThis) {
// make sure the new position in in bounds
nsView* scrolledView = GetScrolledView();
#ifdef DEBUG_pollmann
NS_ASSERTION(scrolledView, "no scrolled view");
#endif
nsView* scrolledView = aThis->GetScrolledView();
if (!scrolledView) return NS_ERROR_FAILURE;
nsSize scrolledSize;
scrolledView->GetDimensions(scrolledSize);
nsSize portSize;
GetDimensions(portSize);
aThis->GetDimensions(portSize);
nscoord maxX = scrolledSize.width - portSize.width;
nscoord maxY = scrolledSize.height - portSize.height;
if (aX > maxX)
aX = maxX;
if (aY > maxY)
aY = maxY;
if (aX < 0)
aX = 0;
if (aY < 0)
aY = 0;
// convert aX and aY in pixels
nscoord aXpx = NSTwipsToIntPixels(aX, t2p);
nscoord aYpx = NSTwipsToIntPixels(aY, t2p);
aX = NSIntPixelsToTwips(aXpx,p2t);
aY = NSIntPixelsToTwips(aYpx,p2t);
return NS_OK;
}
/*
* this method wraps calls to ScrollToImpl(), either in one shot or incrementally,
* based on the setting of the smooth scroll pref
*/
NS_IMETHODIMP nsScrollPortView::ScrollTo(nscoord aDestinationX, nscoord aDestinationY,
PRUint32 aUpdateFlags)
{
// do nothing if the we aren't scrolling.
// this needs to be rechecked because of the clamping and
// rounding
if (aX == mOffsetX && aY == mOffsetY)
if (aDestinationX == mOffsetX && aDestinationY == mOffsetY) {
// kill any in-progress smooth scroll
delete mSmoothScroll;
mSmoothScroll = nsnull;
return NS_OK;
}
if ((aUpdateFlags & NS_VMREFRESH_SMOOTHSCROLL) == 0
|| !IsSmoothScrollingEnabled()) {
// Smooth scrolling is not allowed, so we'll kill any existing smooth-scrolling process
// and do an instant scroll
delete mSmoothScroll;
mSmoothScroll = nsnull;
return ScrollToImpl(aDestinationX, aDestinationY, aUpdateFlags);
}
// figure out the diff by comparing old pos to new
dxPx = mOffsetXpx - aXpx;
dyPx = mOffsetYpx - aYpx;
// notify the listeners.
PRUint32 listenerCount;
const nsIID& kScrollPositionListenerIID = NS_GET_IID(nsIScrollPositionListener);
nsIScrollPositionListener* listener;
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->ScrollPositionWillChange(this, aX, aY);
NS_RELEASE(listener);
}
PRInt32 currentVelocityX;
PRInt32 currentVelocityY;
if (mSmoothScroll) {
currentVelocityX = mSmoothScroll->mVelocities[mSmoothScroll->mFrameIndex*2];
currentVelocityY = mSmoothScroll->mVelocities[mSmoothScroll->mFrameIndex*2 + 1];
} else {
currentVelocityX = 0;
currentVelocityY = 0;
mSmoothScroll = new SmoothScroll;
if (mSmoothScroll) {
mSmoothScroll->mScrollAnimationTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!mSmoothScroll->mScrollAnimationTimer) {
delete mSmoothScroll;
mSmoothScroll = nsnull;
} else {
mSmoothScroll->mScrollAnimationTimer->InitWithFuncCallback(
SmoothScrollAnimationCallback, this, SMOOTH_SCROLL_MSECS_PER_FRAME,
nsITimer::TYPE_REPEATING_PRECISE);
}
}
}
if (nsnull != scrolledView)
{
// move the scrolled view to the new location
scrolledView->SetPosition(-aX, -aY);
// store old position in pixels. We need to do this to make sure there is no
// round off errors. This could cause weird scrolling.
mOffsetXpx = aXpx;
mOffsetYpx = aYpx;
// store the new position
mOffsetX = aX;
mOffsetY = aY;
}
Scroll(scrolledView, dxPx, dyPx, t2p, 0);
// notify the listeners.
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->ScrollPositionDidChange(this, aX, aY);
NS_RELEASE(listener);
}
}
if (!mSmoothScroll) {
// some allocation failed. Scroll the normal way.
return ScrollToImpl(aDestinationX, aDestinationY, aUpdateFlags);
}
mSmoothScroll->mDestinationX = mOffsetX;
mSmoothScroll->mDestinationY = mOffsetY;
mSmoothScroll->mVelocities = new PRInt32[SMOOTH_SCROLL_FRAMES*2];
}
// need to store these so we know when to stop scrolling
// Treat the desired scroll destination as an offset
// relative to the current position. This makes things
// work when someone starts a smooth scroll
// while an existing smooth scroll has not yet been
// completed.
mSmoothScroll->mDestinationX += aDestinationX - mOffsetX;
mSmoothScroll->mDestinationY += aDestinationY - mOffsetY;
mSmoothScroll->mFrameIndex = 0;
ClampScrollValues(mSmoothScroll->mDestinationX, mSmoothScroll->mDestinationY, this);
// compute velocity vectors
ComputeVelocities(currentVelocityX, mOffsetX,
mSmoothScroll->mDestinationX, mSmoothScroll->mVelocities);
ComputeVelocities(currentVelocityY, mOffsetY,
mSmoothScroll->mDestinationY, mSmoothScroll->mVelocities + 1);
// call a scroll immediately, don't wait for the timer to callback.
// this improves responsiveness
IncrementalScroll();
return NS_OK;
}
@ -447,9 +524,7 @@ NS_IMETHODIMP nsScrollPortView::ScrollByLines(PRInt32 aNumLinesX, PRInt32 aNumLi
nscoord dx = mLineHeight*aNumLinesX;
nscoord dy = mLineHeight*aNumLinesY;
ScrollTo(mOffsetX + dx, mOffsetY + dy, 0);
return NS_OK;
return ScrollTo(mOffsetX + dx, mOffsetY + dy, NS_VMREFRESH_SMOOTHSCROLL);
}
NS_IMETHODIMP nsScrollPortView::ScrollByPages(PRInt32 aNumPagesX, PRInt32 aNumPagesY)
@ -465,9 +540,7 @@ NS_IMETHODIMP nsScrollPortView::ScrollByPages(PRInt32 aNumPagesX, PRInt32 aNumPa
dx *= aNumPagesX;
dy *= aNumPagesY;
ScrollTo(mOffsetX + dx, mOffsetY + dy, 0);
return NS_OK;
return ScrollTo(mOffsetX + dx, mOffsetY + dy, NS_VMREFRESH_SMOOTHSCROLL);
}
NS_IMETHODIMP nsScrollPortView::ScrollByWhole(PRBool aTop)
@ -564,3 +637,142 @@ NS_IMETHODIMP nsScrollPortView::Paint(nsIRenderingContext& aRC, const nsIRegion&
return rv;
}
NS_IMETHODIMP nsScrollPortView::ScrollToImpl(nscoord aX, nscoord aY, PRUint32 aUpdateFlags)
{
PRInt32 dxPx = 0, dyPx = 0;
// convert to pixels
nsIDeviceContext *dev;
float t2p;
float p2t;
mViewManager->GetDeviceContext(dev);
dev->GetAppUnitsToDevUnits(t2p);
dev->GetDevUnitsToAppUnits(p2t);
NS_RELEASE(dev);
// Update the scrolled view's position
nsresult rv = ClampScrollValues(aX, aY, this);
if (NS_FAILED(rv)) {
return rv;
}
// convert aX and aY in pixels
nscoord aXpx = NSTwipsToIntPixels(aX, t2p);
nscoord aYpx = NSTwipsToIntPixels(aY, t2p);
aX = NSIntPixelsToTwips(aXpx,p2t);
aY = NSIntPixelsToTwips(aYpx,p2t);
// do nothing if the we aren't scrolling.
// this needs to be rechecked because of the clamping and
// rounding
if (aX == mOffsetX && aY == mOffsetY) {
return NS_OK;
}
// figure out the diff by comparing old pos to new
dxPx = mOffsetXpx - aXpx;
dyPx = mOffsetYpx - aYpx;
// notify the listeners.
PRUint32 listenerCount;
const nsIID& kScrollPositionListenerIID = NS_GET_IID(nsIScrollPositionListener);
nsIScrollPositionListener* listener;
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->ScrollPositionWillChange(this, aX, aY);
NS_RELEASE(listener);
}
}
}
}
nsView* scrolledView = GetScrolledView();
if (!scrolledView) return NS_ERROR_FAILURE;
// move the scrolled view to the new location
scrolledView->SetPosition(-aX, -aY);
// store old position in pixels. We need to do this to make sure there is no
// round off errors. This could cause weird scrolling.
mOffsetXpx = aXpx;
mOffsetYpx = aYpx;
// store the new position
mOffsetX = aX;
mOffsetY = aY;
Scroll(scrolledView, dxPx, dyPx, t2p, 0);
// notify the listeners.
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->ScrollPositionDidChange(this, aX, aY);
NS_RELEASE(listener);
}
}
}
}
return NS_OK;
}
/************************
*
* smooth scrolling methods
*
***********************/
PRBool nsScrollPortView::IsSmoothScrollingEnabled() {
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs) {
PRBool enabled;
nsresult rv = prefs->GetBoolPref(SMOOTH_SCROLL_PREF_NAME, &enabled);
if (NS_SUCCEEDED(rv)) {
return enabled;
}
}
return PR_FALSE;
}
/*
* Callback function from timer used in nsScrollPortView::DoSmoothScroll
* this cleans up the target coordinates and incrementally calls
* nsScrollPortView::ScrollTo
*/
void
nsScrollPortView::SmoothScrollAnimationCallback (nsITimer *aTimer, void* anInstance)
{
nsScrollPortView* self = NS_STATIC_CAST(nsScrollPortView*, anInstance);
if (self) {
self->IncrementalScroll();
}
}
/*
* manages data members and calls to ScrollTo from the (static) SmoothScrollAnimationCallback method
*/
void
nsScrollPortView::IncrementalScroll()
{
if (!mSmoothScroll) {
return;
}
if (mSmoothScroll->mFrameIndex < SMOOTH_SCROLL_FRAMES) {
ScrollToImpl(mOffsetX + mSmoothScroll->mVelocities[mSmoothScroll->mFrameIndex*2],
mOffsetY + mSmoothScroll->mVelocities[mSmoothScroll->mFrameIndex*2 + 1],
0);
mSmoothScroll->mFrameIndex++;
} else {
delete mSmoothScroll;
mSmoothScroll = nsnull;
}
}

View File

@ -40,6 +40,8 @@
#include "nsView.h"
#include "nsIScrollableView.h"
#include "nsCOMPtr.h"
#include "nsITimer.h"
class nsISupportsArray;
@ -99,6 +101,30 @@ public:
private:
NS_IMETHOD_(nsrefcnt) AddRef(void);
NS_IMETHOD_(nsrefcnt) Release(void);
NS_IMETHOD ScrollToImpl(nscoord aX, nscoord aY, PRUint32 aUpdateFlags);
class SmoothScroll {
public:
SmoothScroll() { mVelocities = nsnull; }
~SmoothScroll() {
if (mScrollAnimationTimer) mScrollAnimationTimer->Cancel();
delete[] mVelocities;
}
nsCOMPtr<nsITimer> mScrollAnimationTimer;
PRInt32 mFrameIndex;
PRInt32* mVelocities;
nscoord mDestinationX;
nscoord mDestinationY;
};
// data members
SmoothScroll* mSmoothScroll;
// methods
void IncrementalScroll();
PRBool IsSmoothScrollingEnabled();
static void SmoothScrollAnimationCallback(nsITimer *aTimer, void* aESM);
protected:
virtual ~nsScrollPortView();
@ -107,7 +133,7 @@ protected:
void AdjustChildWidgets(nsScrollPortView *aScrolling, nsView *aView, nscoord aDx, nscoord aDy, float aScale);
void Scroll(nsView *aScrolledView, PRInt32 aDx, PRInt32 aDy, float scale, PRUint32 aUpdateFlags);
PRBool CannotBitBlt(nsView* aScrolledView);
protected:
nscoord mOffsetX, mOffsetY;
nscoord mOffsetXpx, mOffsetYpx;
PRUint32 mScrollProperties;