mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 174049. Smooth scrolling. Original patch by Neil cronin. r+sr=kin
This commit is contained in:
parent
4f81dc78d1
commit
64131124e6
@ -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")
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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___
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user