319078 Handle smooth mousewheel (and two-finger touchpad) scrolling. r=josh sr=darin r,sr=roc

git-svn-id: svn://10.0.0.236/trunk@203254 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
mark%moxienet.com 2006-07-20 15:38:43 +00:00
parent 54fe7a55b2
commit ddab7ffe6e
10 changed files with 242 additions and 56 deletions

View File

@ -172,7 +172,8 @@ enum {
MOUSE_SCROLL_N_LINES,
MOUSE_SCROLL_PAGE,
MOUSE_SCROLL_HISTORY,
MOUSE_SCROLL_TEXTSIZE
MOUSE_SCROLL_TEXTSIZE,
MOUSE_SCROLL_PIXELS
};
// mask values for ui.key.chromeAccess and ui.key.contentAccess
@ -1817,7 +1818,7 @@ nsEventStateManager::DoScrollText(nsPresContext* aPresContext,
nsInputEvent* aEvent,
PRInt32 aNumLines,
PRBool aScrollHorizontal,
PRBool aScrollPage)
ScrollQuantity aScrollQuantity)
{
nsCOMPtr<nsIContent> targetContent = aTargetFrame->GetContent();
if (!targetContent)
@ -1838,7 +1839,7 @@ nsEventStateManager::DoScrollText(nsPresContext* aPresContext,
nsCOMPtr<nsIDOMAbstractView> view;
docView->GetDefaultView(getter_AddRefs(view));
if (aScrollPage) {
if (aScrollQuantity == eScrollByPage) {
if (aNumLines > 0) {
aNumLines = nsIDOMNSUIEvent::SCROLL_PAGE_DOWN;
} else {
@ -1963,7 +1964,7 @@ nsEventStateManager::DoScrollText(nsPresContext* aPresContext,
PRInt32 scrollX = 0;
PRInt32 scrollY = aNumLines;
if (aScrollPage)
if (aScrollQuantity == eScrollByPage)
scrollY = (scrollY > 0) ? 1 : -1;
if (aScrollHorizontal) {
@ -1971,8 +1972,10 @@ nsEventStateManager::DoScrollText(nsPresContext* aPresContext,
scrollY = 0;
}
if (aScrollPage)
if (aScrollQuantity == eScrollByPage)
scrollView->ScrollByPages(scrollX, scrollY);
else if (aScrollQuantity == eScrollByPixel)
scrollView->ScrollByPixels(scrollX, scrollY);
else
scrollView->ScrollByLines(scrollX, scrollY);
@ -1986,7 +1989,7 @@ nsEventStateManager::DoScrollText(nsPresContext* aPresContext,
*getter_AddRefs(newPresContext));
if (NS_SUCCEEDED(rv) && newFrame)
return DoScrollText(newPresContext, newFrame, aEvent, aNumLines,
aScrollHorizontal, aScrollPage);
aScrollHorizontal, aScrollQuantity);
}
return NS_OK;
@ -2219,6 +2222,8 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
numLines = msEvent->delta;
if (msEvent->scrollFlags & nsMouseScrollEvent::kIsFullPage)
action = MOUSE_SCROLL_PAGE;
else if (msEvent->scrollFlags & nsMouseScrollEvent::kIsPixels)
action = MOUSE_SCROLL_PIXELS;
}
else
{
@ -2253,13 +2258,27 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
switch (action) {
case MOUSE_SCROLL_N_LINES:
{
DoScrollText(presContext, aTargetFrame, msEvent, numLines,
(msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
eScrollByLine);
}
break;
case MOUSE_SCROLL_PAGE:
{
DoScrollText(presContext, aTargetFrame, msEvent, numLines,
(msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
(action == MOUSE_SCROLL_PAGE));
eScrollByPage);
}
break;
case MOUSE_SCROLL_PIXELS:
{
DoScrollText(presContext, aTargetFrame, msEvent, numLines,
(msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
eScrollByPixel);
}
break;
case MOUSE_SCROLL_HISTORY:

View File

@ -245,12 +245,18 @@ protected:
nsPresContext* aPresContext,
nsIFrame* &targetOuterFrame,
nsPresContext* &presCtxOuter);
typedef enum {
eScrollByPixel,
eScrollByLine,
eScrollByPage
} ScrollQuantity;
nsresult DoScrollText(nsPresContext* aPresContext,
nsIFrame* aTargetFrame,
nsInputEvent* aEvent,
PRInt32 aNumLines,
PRBool aScrollHorizontal,
PRBool aScrollPage);
ScrollQuantity aScrollQuantity);
void ForceViewUpdate(nsIView* aView);
void DoScrollHistory(PRInt32 direction);
void DoScrollTextsize(nsIFrame *aTargetFrame, PRInt32 adjustment);

View File

@ -49,8 +49,8 @@ struct nsSize;
// IID for the nsIScrollableView interface
#define NS_ISCROLLABLEVIEW_IID \
{ 0x36083bcf, 0x61d7, 0x4c24, \
{ 0xa6, 0xd4, 0x2f, 0x05, 0xba, 0x2c, 0x1b, 0x51 } }
{ 0x1fcd151c, 0x5e26, 0x4c9d, \
{ 0xa5, 0x2c, 0x87, 0x43, 0x7d, 0x7b, 0x1c, 0xe8 } }
/**
* A scrolling view allows an arbitrary view that you supply to be scrolled
@ -175,6 +175,16 @@ public:
*/
NS_IMETHOD ScrollByWhole(PRBool aTop) = 0;
/**
* Scroll the view left or right by aNumLinesX pixels. Positive values move
* right. Scroll the view up or down by aNumLinesY pixels. Positive values
* move down. Prevents scrolling off the end of the view.
* @param aNumLinesX number of lines to scroll the view horizontally
* @param aNumLinesY number of lines to scroll the view vertically
* @return error status
*/
NS_IMETHOD ScrollByPixels(PRInt32 aNumPixelsX, PRInt32 aNumPixelsY) = 0;
/**
* Check the view can scroll from current offset.
* @param aHorizontal If checking to Left or to Right, true. Otherwise, false.

View File

@ -468,6 +468,19 @@ NS_IMETHODIMP nsScrollPortView::ScrollByWhole(PRBool aTop)
return NS_OK;
}
NS_IMETHODIMP nsScrollPortView::ScrollByPixels(PRInt32 aNumPixelsX,
PRInt32 aNumPixelsY)
{
nsCOMPtr<nsIDeviceContext> dev;
mViewManager->GetDeviceContext(*getter_AddRefs(dev));
float p2t = dev->DevUnitsToAppUnits();
nscoord dx = NSIntPixelsToTwips(aNumPixelsX, p2t);
nscoord dy = NSIntPixelsToTwips(aNumPixelsY, p2t);
return ScrollTo(mOffsetX + dx, mOffsetY + dy, 0);
}
NS_IMETHODIMP nsScrollPortView::CanScroll(PRBool aHorizontal,
PRBool aForward,
PRBool &aResult)

View File

@ -77,6 +77,7 @@ public:
NS_IMETHOD GetPageScrollDistances(nsSize *aDistances);
NS_IMETHOD ScrollByPages(PRInt32 aNumPagesX, PRInt32 aNumPagesY);
NS_IMETHOD ScrollByWhole(PRBool aTop);
NS_IMETHOD ScrollByPixels(PRInt32 aNumPixelsX, PRInt32 aNumPixelsY);
NS_IMETHOD CanScroll(PRBool aHorizontal, PRBool aForward, PRBool &aResult);
NS_IMETHOD_(nsIView*) View();

View File

@ -748,7 +748,8 @@ public:
enum nsMouseScrollFlags {
kIsFullPage = 1 << 0,
kIsVertical = 1 << 1,
kIsHorizontal = 1 << 2
kIsHorizontal = 1 << 2,
kIsPixels = 1 << 3
};
nsMouseScrollEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)

View File

@ -1269,9 +1269,27 @@ PRBool nsMacEventHandler::ResizeEvent ( WindowRef inWindow )
// Called from a mouseWheel carbon event, tell Gecko to scroll.
//
PRBool
nsMacEventHandler::Scroll(EventMouseWheelAxis inAxis, PRInt32 inDelta,
const Point& inMouseLoc, nsWindow* inWindow,
PRUint32 inModifiers) {
nsMacEventHandler::Scroll(PRInt32 aDeltaY, PRInt32 aDeltaX,
PRBool aIsPixels, const Point& aMouseLoc,
nsWindow* aWindow, PRUint32 aModifiers) {
PRBool resY = ScrollAxis(nsMouseScrollEvent::kIsVertical, aDeltaY,
aIsPixels, aMouseLoc, aWindow, aModifiers);
PRBool resX = ScrollAxis(nsMouseScrollEvent::kIsHorizontal, aDeltaX,
aIsPixels, aMouseLoc, aWindow, aModifiers);
return resY || resX;
} // Scroll
//
// ScrollAxis
//
PRBool
nsMacEventHandler::ScrollAxis(nsMouseScrollEvent::nsMouseScrollFlags aAxis,
PRInt32 aDelta, PRBool aIsPixels,
const Point& aMouseLoc, nsWindow* aWindow,
PRUint32 aModifiers)
{
// Only scroll active windows. Treat popups as active.
WindowRef windowRef = NS_STATIC_CAST(WindowRef,
mTopLevelWidget->GetNativeData(NS_NATIVE_DISPLAY));
@ -1281,16 +1299,21 @@ nsMacEventHandler::Scroll(EventMouseWheelAxis inAxis, PRInt32 inDelta,
return PR_FALSE;
// Figure out which widget should be scrolled by traversing the widget
// hierarchy beginning at the root nsWindow. inMouseLoc should be
// hierarchy beginning at the root nsWindow. aMouseLoc should be
// relative to the origin of this nsWindow. If the scroll event came
// from an nsMacWindow, then inWindow should refer to that nsMacWindow.
nsIWidget* widgetToScroll = inWindow->FindWidgetHit(inMouseLoc);
// from an nsMacWindow, then aWindow should refer to that nsMacWindow.
nsIWidget* widgetToScroll = aWindow->FindWidgetHit(aMouseLoc);
// Not all scroll events for the window are over a widget. Consider
// the title bar.
if (!widgetToScroll)
return PR_FALSE;
if (aDelta == 0) {
// Don't need to do anything, but eat the event anyway.
return PR_TRUE;
}
if (gRollupListener && gRollupWidget) {
// Roll up the rollup widget if the scroll isn't targeted at it
// (or one of its children) and the listener was told to do so.
@ -1316,22 +1339,23 @@ nsMacEventHandler::Scroll(EventMouseWheelAxis inAxis, PRInt32 inDelta,
// The direction we get from the carbon event is opposite from the way
// mozilla looks at it. Reverse the direction.
scrollEvent.delta = -inDelta;
scrollEvent.delta = -aDelta;
// If the scroll event comes from a mouse that only has a scroll wheel for
// the vertical axis, and the shift key is held down, the system presents
// it as a horizontal scroll and doesn't clear the shift key bit from
// inModifiers. The Mac is supposed to scroll horizontally in such a case.
// aModifiers. The Mac is supposed to scroll horizontally in such a case.
//
// If the scroll event comes from a mouse that can scroll both axes, the
// system doesn't apply any of this shift-key fixery.
scrollEvent.scrollFlags =
(inAxis == kEventMouseWheelAxisX) ? nsMouseScrollEvent::kIsHorizontal :
nsMouseScrollEvent::kIsVertical;
scrollEvent.scrollFlags = aAxis;
if (aIsPixels)
scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsPixels;
// convert window-relative (local) mouse coordinates to widget-relative
// coords for Gecko.
nsPoint mouseLocRelativeToWidget(inMouseLoc.h, inMouseLoc.v);
nsPoint mouseLocRelativeToWidget(aMouseLoc.h, aMouseLoc.v);
nsRect bounds;
widgetToScroll->GetBounds(bounds);
nsPoint widgetOrigin(bounds.x, bounds.y);
@ -1343,16 +1367,16 @@ nsMacEventHandler::Scroll(EventMouseWheelAxis inAxis, PRInt32 inDelta,
scrollEvent.time = PR_IntervalNow();
// Translate OS event modifiers into Gecko event modifiers
scrollEvent.isShift = ((inModifiers & shiftKey) != 0);
scrollEvent.isControl = ((inModifiers & controlKey) != 0);
scrollEvent.isAlt = ((inModifiers & optionKey) != 0);
scrollEvent.isMeta = ((inModifiers & cmdKey) != 0);
scrollEvent.isShift = ((aModifiers & shiftKey) != 0);
scrollEvent.isControl = ((aModifiers & controlKey) != 0);
scrollEvent.isAlt = ((aModifiers & optionKey) != 0);
scrollEvent.isMeta = ((aModifiers & cmdKey) != 0);
nsEventStatus status;
widgetToScroll->DispatchEvent(&scrollEvent, status);
return nsWindow::ConvertStatus(status);
} // Scroll
} // ScrollAxis
//-------------------------------------------------------------------------

View File

@ -132,7 +132,10 @@ public:
// not have to rely on hacking up EventRecords to fake it.
//
virtual PRBool ResizeEvent ( WindowRef inWindow ) ;
virtual PRBool Scroll ( EventMouseWheelAxis inAxis, PRInt32 inDelta, const Point& inMouseLoc, nsWindow* inWindow, PRUint32 inModifiers );
virtual PRBool Scroll (PRInt32 aDeltaY, PRInt32 aDeltaX,
PRBool aIsPixels,
const Point& aMouseLoc,
nsWindow* aWindow, PRUint32 aModifiers);
virtual void HandleActivateEvent(EventRef aEvent);
inline nsMacEventDispatchHandler* GetEventDispatchHandler() { return mEventDispatchHandler; }
@ -157,6 +160,11 @@ protected:
virtual nsresult HandleStartComposition(void);
virtual nsresult HandleEndComposition(void);
virtual nsresult HandleTextEvent(PRUint32 textRangeCount, nsTextRangeArray textRangeArray);
virtual PRBool ScrollAxis (nsMouseScrollEvent::nsMouseScrollFlags aAxis,
PRInt32 aDelta, PRBool aIsPixels,
const Point& aMouseLoc,
nsWindow* aWindow,
PRUint32 aModifiers);
protected:
nsMacEventDispatchHandler* mEventDispatchHandler;

View File

@ -66,6 +66,21 @@ typedef struct TransitionWindowOptions {
WindowRef window;
void* userData;
} TransitionWindowOptions;
#define kEventParamWindowPartCode 'wpar'
#define typeWindowPartCode 'wpar'
#endif
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
// http://developer.apple.com/qa/qa2005/qa1453.html
// These are not defined in early versions of the 10.4/10.4u SDK (as of Xcode
// 2.2), but they may appear in later releases. Since we can't check which
// version of the SDK is in use beyond knowing that it's "10.4", define these
// on 10.4. #defines should override what's present in the SDK when defined,
// because the system headers use enums.
#define kEventParamMouseWheelSmoothVerticalDelta 'saxy'
#define kEventParamMouseWheelSmoothHorizontalDelta 'saxx'
#define kEventMouseScroll 11
#endif
typedef OSStatus (*TransitionWindowWithOptions_type) (WindowRef,
@ -228,6 +243,7 @@ nsMacWindow::nsMacWindow() : Inherited()
, mResizeIsFromUs(PR_FALSE)
, mShown(PR_FALSE)
, mSheetNeedsShow(PR_FALSE)
, mInPixelMouseScroll(PR_FALSE)
, mMacEventHandler(nsnull)
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_3
, mNeedsResize(PR_FALSE)
@ -600,6 +616,7 @@ nsresult nsMacWindow::StandardCreate(nsIWidget *aParent,
mWindowType != eWindowType_java) {
const EventTypeSpec kScrollEventList[] = {
{ kEventClassMouse, kEventMouseWheelMoved },
{ kEventClassMouse, kEventMouseScroll },
};
static EventHandlerUPP sScrollEventHandlerUPP;
@ -693,37 +710,121 @@ nsresult nsMacWindow::StandardCreate(nsIWidget *aParent,
pascal OSStatus
nsMacWindow::ScrollEventHandler ( EventHandlerCallRef inHandlerChain, EventRef inEvent, void* userData )
nsMacWindow::ScrollEventHandler(EventHandlerCallRef aHandlerCallRef,
EventRef aEvent,
void* aUserData)
{
OSStatus retVal = eventNotHandledErr;
EventMouseWheelAxis axis = kEventMouseWheelAxisY;
SInt32 delta = 0;
Point mouseLoc;
UInt32 modifiers = 0;
if (::GetEventParameter(inEvent, kEventParamMouseWheelAxis,
typeMouseWheelAxis, NULL,
sizeof(EventMouseWheelAxis), NULL, &axis) == noErr &&
::GetEventParameter(inEvent, kEventParamMouseWheelDelta,
typeLongInteger, NULL,
sizeof(SInt32), NULL, &delta) == noErr &&
::GetEventParameter(inEvent, kEventParamMouseLocation,
if (::GetEventParameter(aEvent, kEventParamMouseLocation,
typeQDPoint, NULL,
sizeof(Point), NULL, &mouseLoc) == noErr &&
::GetEventParameter(inEvent, kEventParamKeyModifiers,
sizeof(Point), NULL, &mouseLoc) != noErr ||
::GetEventParameter(aEvent, kEventParamKeyModifiers,
typeUInt32, NULL,
sizeof(UInt32), NULL, &modifiers) == noErr) {
nsMacWindow* self = NS_REINTERPRET_CAST(nsMacWindow*, userData);
NS_ENSURE_TRUE(self->mMacEventHandler.get(), eventNotHandledErr);
if (self) {
// Convert mouse to local coordinates since that's how the event handler
// wants them
StPortSetter portSetter(self->mWindowPtr);
StOriginSetter originSetter(self->mWindowPtr);
::GlobalToLocal(&mouseLoc);
self->mMacEventHandler->Scroll(axis, delta, mouseLoc, self, modifiers);
retVal = noErr;
sizeof(UInt32), NULL, &modifiers) != noErr) {
// Need both of these params regardless of event kind.
return retVal;
}
SInt32 deltaY = 0, deltaX = 0;
PRBool isPixels = PR_FALSE;
nsMacWindow* self = NS_REINTERPRET_CAST(nsMacWindow*, aUserData);
EventKind kind = ::GetEventKind(aEvent);
switch (kind) {
case kEventMouseWheelMoved: {
// Notchy scrolling hardware, like conventional wheel mice, scroll
// a line at a time.
if (self->mInPixelMouseScroll) {
// A smooth scroll was already done by pixels. Don't scroll again by
// lines.
return noErr;
}
EventMouseWheelAxis axis = kEventMouseWheelAxisY;
SInt32 delta = 0;
if (::GetEventParameter(aEvent, kEventParamMouseWheelAxis,
typeMouseWheelAxis, NULL,
sizeof(EventMouseWheelAxis), NULL,
&axis) != noErr ||
::GetEventParameter(aEvent, kEventParamMouseWheelDelta,
typeLongInteger, NULL,
sizeof(SInt32), NULL, &delta) != noErr) {
// Need both of these params.
return retVal;
}
if (axis == kEventMouseWheelAxisY)
deltaY = delta;
else
deltaX = delta;
break;
}
case kEventMouseScroll: {
// On Tiger or later, smooth scrolling hardware, like Apple touchpads
// and Mighty Mice, scroll a pixel at a time. Handling this event means
// that the system won't send a kEventMouseWheelMoved event.
isPixels = PR_TRUE;
OSErr errY, errX;
errY = ::GetEventParameter(aEvent,
kEventParamMouseWheelSmoothVerticalDelta,
typeSInt32, NULL,
sizeof(SInt32), NULL, &deltaY);
errX = ::GetEventParameter(aEvent,
kEventParamMouseWheelSmoothHorizontalDelta,
typeSInt32, NULL,
sizeof(SInt32), NULL, &deltaX);
if (errY != noErr && errX != noErr) {
// Need at least one of these params.
return retVal;
}
if ((errY != noErr && errY != eventParameterNotFoundErr) ||
(errX != noErr && errX != eventParameterNotFoundErr)) {
// eventParameterNotFoundErr is the only permissible "error",
// in that case, leave the delta set to 0.
return retVal;
}
break;
}
default: {
// What?
return retVal;
break;
}
}
{
// Convert mouse to local coordinates since that's how the event handler
// wants them
StPortSetter portSetter(self->mWindowPtr);
StOriginSetter originSetter(self->mWindowPtr);
::GlobalToLocal(&mouseLoc);
self->mMacEventHandler->Scroll(deltaY, deltaX, isPixels, mouseLoc,
self, modifiers);
retVal = noErr;
}
if (kind == kEventMouseScroll && (deltaX != 0 || deltaY != 0)) {
// Plug-ins might depend on kEventMouseWheelMoved, so if the event was
// kEventMouseScroll, call to the next handler to give the system the
// chance to turn one event into the other. See
// http://developer.apple.com/qa/qa2005/qa1453.html . When the
// new event reaches this handler, mInPixelMouseScroll will prevent
// double-scrolling.
PRBool lastInPixelMouseScroll = self->mInPixelMouseScroll;
self->mInPixelMouseScroll = PR_TRUE;
::CallNextEventHandler(aHandlerCallRef, aEvent);
self->mInPixelMouseScroll = lastInPixelMouseScroll;
}
return retVal;
} // ScrollEventHandler
@ -1930,8 +2031,9 @@ nsMacWindow::Scroll ( PRBool aVertical, PRInt16 aNumLines, PRInt16 aMouseLocalX,
*_retval = PR_FALSE;
NS_ENSURE_TRUE(mMacEventHandler.get(), NS_ERROR_FAILURE);
Point localPoint = {aMouseLocalY, aMouseLocalX};
*_retval = mMacEventHandler->Scroll(aVertical ? kEventMouseWheelAxisY : kEventMouseWheelAxisX,
aNumLines, localPoint, this, 0);
*_retval = mMacEventHandler->Scroll(aVertical ? aNumLines : 0,
aVertical ? 0 : aNumLines,
PR_FALSE, localPoint, this, 0);
return NS_OK;
}

View File

@ -146,8 +146,9 @@ protected:
pascal static OSStatus WindowEventHandler ( EventHandlerCallRef inHandlerChain,
EventRef inEvent, void* userData ) ;
pascal static OSStatus ScrollEventHandler ( EventHandlerCallRef inHandlerChain,
EventRef inEvent, void* userData ) ;
pascal static OSStatus ScrollEventHandler(EventHandlerCallRef aHandlerCallRef,
EventRef aEvent,
void* aUserData);
pascal static OSStatus KeyEventHandler(EventHandlerCallRef aHandlerCallRef,
EventRef aEvent,
void* aUserData);
@ -162,6 +163,7 @@ protected:
PRPackedBool mResizeIsFromUs; // we originated the resize, prevent infinite recursion
PRPackedBool mShown; // whether the window was actually shown on screen
PRPackedBool mSheetNeedsShow; // a sheet that wants to be displayed but isn't because a sibling sheet is open
PRPackedBool mInPixelMouseScroll;
Point mBoundsOffset; // offset from window structure to content
auto_ptr<nsMacEventHandler> mMacEventHandler;
nsIWidget *mOffsetParent;