mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Bug 668953 - Support two-finger horizontal swipe on OS X Lion. r=bgirard
This commit is contained in:
parent
58c0c20c7a
commit
21ffbbde09
@ -141,10 +141,6 @@
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#import <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
#include "nsCocoaFeatures.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
@ -377,7 +373,6 @@ public:
|
||||
static void OnEvent(nsEvent* aEvent);
|
||||
static void Shutdown();
|
||||
static PRUint32 GetTimeoutTime();
|
||||
static PRUint32 GetGestureTimeoutTime();
|
||||
static PRInt32 AccelerateWheelDelta(PRInt32 aScrollLines,
|
||||
PRBool aIsHorizontal, PRBool aAllowScrollSpeedOverride,
|
||||
nsIScrollableFrame::ScrollUnit *aScrollQuantity,
|
||||
@ -387,10 +382,6 @@ public:
|
||||
enum {
|
||||
kScrollSeriesTimeout = 80
|
||||
};
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
static PRBool GetGestureTriggered();
|
||||
static void SetGestureTriggered();
|
||||
#endif
|
||||
protected:
|
||||
static nsIntPoint GetScreenPoint(nsGUIEvent* aEvent);
|
||||
static void OnFailToScrollTarget();
|
||||
@ -411,9 +402,6 @@ protected:
|
||||
static PRUint32 sMouseMoved; // in milliseconds
|
||||
static nsITimer* sTimer;
|
||||
static PRInt32 sScrollSeriesCounter;
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
static PRUint32 sGestureTriggered; // in milliseconds
|
||||
#endif
|
||||
};
|
||||
|
||||
nsWeakFrame nsMouseWheelTransaction::sTargetFrame(nsnull);
|
||||
@ -421,9 +409,6 @@ PRUint32 nsMouseWheelTransaction::sTime = 0;
|
||||
PRUint32 nsMouseWheelTransaction::sMouseMoved = 0;
|
||||
nsITimer* nsMouseWheelTransaction::sTimer = nsnull;
|
||||
PRInt32 nsMouseWheelTransaction::sScrollSeriesCounter = 0;
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
PRUint32 nsMouseWheelTransaction::sGestureTriggered = 0;
|
||||
#endif
|
||||
|
||||
static PRBool
|
||||
OutOfTime(PRUint32 aBaseTime, PRUint32 aThreshold)
|
||||
@ -502,29 +487,8 @@ nsMouseWheelTransaction::EndTransaction()
|
||||
sTimer->Cancel();
|
||||
sTargetFrame = nsnull;
|
||||
sScrollSeriesCounter = 0;
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
sGestureTriggered = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
void
|
||||
nsMouseWheelTransaction::SetGestureTriggered() {
|
||||
sGestureTriggered = PR_IntervalToMilliseconds(PR_IntervalNow());
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsMouseWheelTransaction::GetGestureTriggered() {
|
||||
if (sGestureTriggered != 0 &&
|
||||
OutOfTime(sGestureTriggered, GetGestureTimeoutTime())) {
|
||||
// Start accepting new gestures
|
||||
sGestureTriggered = 0;
|
||||
}
|
||||
|
||||
return sGestureTriggered != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::OnEvent(nsEvent* aEvent)
|
||||
{
|
||||
@ -670,12 +634,6 @@ nsMouseWheelTransaction::GetScreenPoint(nsGUIEvent* aEvent)
|
||||
return aEvent->refPoint + aEvent->widget->WidgetToScreenOffset();
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsMouseWheelTransaction::GetGestureTimeoutTime()
|
||||
{
|
||||
return Preferences::GetUint("mousewheel.transaction.gesturetimeout", 300);
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsMouseWheelTransaction::GetTimeoutTime()
|
||||
{
|
||||
@ -2839,22 +2797,6 @@ nsEventStateManager::DoScrollText(nsIFrame* aTargetFrame,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
// On lion scroll will trigger back/forward at the edge of the page
|
||||
if (isHorizontal && passToParent && nsCocoaFeatures::OnLionOrLater()) {
|
||||
if (!nsMouseWheelTransaction::GetGestureTriggered()) {
|
||||
if (numLines > 4 || numLines < -4) {
|
||||
DoScrollHistory(-numLines);
|
||||
nsMouseWheelTransaction::SetGestureTriggered();
|
||||
return NS_OK;
|
||||
}
|
||||
} else {
|
||||
// Extend the gesture in progress
|
||||
nsMouseWheelTransaction::SetGestureTriggered();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!passToParent && frameToScroll) {
|
||||
if (aScrollQuantity == nsIScrollableFrame::LINES) {
|
||||
// When this is called for querying the scroll target information,
|
||||
|
@ -1560,6 +1560,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
nsSimpleGestureEvent(const nsSimpleGestureEvent& other)
|
||||
: nsMouseEvent_base((other.flags & NS_EVENT_FLAG_TRUSTED) != 0,
|
||||
other.message, other.widget, NS_SIMPLE_GESTURE_EVENT),
|
||||
direction(other.direction), delta(other.delta)
|
||||
{
|
||||
}
|
||||
|
||||
PRUint32 direction; // See nsIDOMSimpleGestureEvent for values
|
||||
PRFloat64 delta; // Delta for magnify and rotate events
|
||||
};
|
||||
|
@ -168,6 +168,49 @@ extern "C" long TSMProcessRawKeyEvent(EventRef carbonEvent);
|
||||
- (long long)_scrollPhase;
|
||||
@end
|
||||
|
||||
// The following section, required to support fluid swipe tracking on OS X 10.7
|
||||
// and up, contains defines/declarations that are only available on 10.7 and up.
|
||||
// [NSEvent trackSwipeEventWithOptions:...] also requires that the compiler
|
||||
// support "blocks"
|
||||
// (http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html)
|
||||
// -- which it does on 10.6 and up (using the 10.6 SDK or higher).
|
||||
#ifdef __LP64__
|
||||
enum {
|
||||
NSEventPhaseNone = 0,
|
||||
NSEventPhaseBegan = 0x1 << 0,
|
||||
NSEventPhaseStationary = 0x1 << 1,
|
||||
NSEventPhaseChanged = 0x1 << 2,
|
||||
NSEventPhaseEnded = 0x1 << 3,
|
||||
NSEventPhaseCancelled = 0x1 << 4,
|
||||
};
|
||||
typedef NSUInteger NSEventPhase;
|
||||
|
||||
enum {
|
||||
NSEventSwipeTrackingLockDirection = 0x1 << 0,
|
||||
NSEventSwipeTrackingClampGestureAmount = 0x1 << 1
|
||||
};
|
||||
typedef NSUInteger NSEventSwipeTrackingOptions;
|
||||
|
||||
enum {
|
||||
NSEventGestureAxisNone = 0,
|
||||
NSEventGestureAxisHorizontal,
|
||||
NSEventGestureAxisVertical
|
||||
};
|
||||
typedef NSInteger NSEventGestureAxis;
|
||||
|
||||
@interface NSEvent (FluidSwipeTracking)
|
||||
+ (BOOL)isSwipeTrackingFromScrollEventsEnabled;
|
||||
- (BOOL)hasPreciseScrollingDeltas;
|
||||
- (CGFloat)scrollingDeltaX;
|
||||
- (CGFloat)scrollingDeltaY;
|
||||
- (NSEventPhase)phase;
|
||||
- (void)trackSwipeEventWithOptions:(NSEventSwipeTrackingOptions)options
|
||||
dampenAmountThresholdMin:(CGFloat)minDampenThreshold
|
||||
max:(CGFloat)maxDampenThreshold
|
||||
usingHandler:(void (^)(CGFloat gestureAmount, NSEventPhase phase, BOOL isComplete, BOOL *stop))trackingHandler;
|
||||
@end
|
||||
#endif // #ifdef __LP64__
|
||||
|
||||
@interface ChildView : NSView<
|
||||
#ifdef ACCESSIBILITY
|
||||
mozAccessible,
|
||||
@ -239,6 +282,11 @@ extern "C" long TSMProcessRawKeyEvent(EventRef carbonEvent);
|
||||
float mCumulativeRotation;
|
||||
|
||||
BOOL mDidForceRefreshOpenGL;
|
||||
|
||||
// Support for fluid swipe tracking.
|
||||
#ifdef __LP64__
|
||||
BOOL *mSwipeAnimationCancelled;
|
||||
#endif
|
||||
}
|
||||
|
||||
// class initialization
|
||||
@ -286,6 +334,12 @@ extern "C" long TSMProcessRawKeyEvent(EventRef carbonEvent);
|
||||
- (void)magnifyWithEvent:(NSEvent *)anEvent;
|
||||
- (void)rotateWithEvent:(NSEvent *)anEvent;
|
||||
- (void)endGestureWithEvent:(NSEvent *)anEvent;
|
||||
|
||||
// Support for fluid swipe tracking.
|
||||
#ifdef __LP64__
|
||||
- (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent
|
||||
scrollOverflow:(PRInt32)overflow;
|
||||
#endif
|
||||
@end
|
||||
|
||||
class ChildViewMouseTracker {
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "prlog.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "nsChildView.h"
|
||||
#include "nsCocoaWindow.h"
|
||||
@ -2012,6 +2013,10 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
mDidForceRefreshOpenGL = NO;
|
||||
|
||||
[self setFocusRingType:NSFocusRingTypeNone];
|
||||
|
||||
#ifdef __LP64__
|
||||
mSwipeAnimationCancelled = nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
// register for things we'll take from other applications
|
||||
@ -2987,6 +2992,126 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
||||
// Support fluid swipe tracking on OS X 10.7 and higher. We must be careful
|
||||
// to only invoke this support on a horizontal two-finger gesture that really
|
||||
// is a swipe (and not a scroll) -- in other words, the app is responsible
|
||||
// for deciding which is which. But once the decision is made, the OS tracks
|
||||
// the swipe until it has finished, and decides whether or not it succeeded.
|
||||
// A swipe has the same functionality as the Back and Forward buttons. For
|
||||
// now swipe animation is unsupported (e.g. no bounces). This method is
|
||||
// partly based on Apple sample code available at
|
||||
// http://developer.apple.com/library/mac/#releasenotes/Cocoa/AppKit.html
|
||||
// (under Fluid Swipe Tracking API).
|
||||
#ifdef __LP64__
|
||||
- (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent
|
||||
scrollOverflow:(PRInt32)overflow
|
||||
{
|
||||
if (!nsToolkit::OnLionOrLater()) {
|
||||
return;
|
||||
}
|
||||
// This method checks whether the AppleEnableSwipeNavigateWithScrolls global
|
||||
// preference is set. If it isn't, fluid swipe tracking is disabled, and a
|
||||
// horizontal two-finger gesture is always a scroll (even in Safari). This
|
||||
// preference can't (currently) be set from the Preferences UI -- only using
|
||||
// 'defaults write'.
|
||||
if (![NSEvent isSwipeTrackingFromScrollEventsEnabled]) {
|
||||
return;
|
||||
}
|
||||
if ([anEvent type] != NSScrollWheel) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If a swipe is currently being tracked kill it -- it's been interrupted by
|
||||
// another gesture or legacy scroll wheel event.
|
||||
if (mSwipeAnimationCancelled && (*mSwipeAnimationCancelled == NO)) {
|
||||
*mSwipeAnimationCancelled = YES;
|
||||
mSwipeAnimationCancelled = nil;
|
||||
}
|
||||
|
||||
// Only initiate tracking if the user has tried to scroll past the edge of
|
||||
// the current page (as indicated by 'overflow' being non-zero). Gecko only
|
||||
// sets nsMouseScrollEvent.scrollOverflow when it's processing
|
||||
// NS_MOUSE_PIXEL_SCROLL events (not NS_MOUSE_SCROLL events).
|
||||
// nsMouseScrollEvent.scrollOverflow only indicates left or right overflow
|
||||
// for horizontal NS_MOUSE_PIXEL_SCROLL events.
|
||||
if (!overflow) {
|
||||
return;
|
||||
}
|
||||
// Only initiate tracking for gestures that have just begun -- otherwise a
|
||||
// scroll to one side of the page can have a swipe tacked on to it.
|
||||
if ([anEvent phase] != NSEventPhaseBegan) {
|
||||
return;
|
||||
}
|
||||
CGFloat deltaX, deltaY;
|
||||
if ([anEvent hasPreciseScrollingDeltas]) {
|
||||
deltaX = [anEvent scrollingDeltaX];
|
||||
deltaY = [anEvent scrollingDeltaY];
|
||||
} else {
|
||||
deltaX = [anEvent deltaX];
|
||||
deltaY = [anEvent deltaY];
|
||||
}
|
||||
// Only initiate tracking for events whose horizontal element is at least
|
||||
// eight times larger than its vertical element. This minimizes performance
|
||||
// problems with vertical scrolls (by minimizing the possibility that they'll
|
||||
// be misinterpreted as horizontal swipes), while still tolerating a small
|
||||
// vertical element to a true horizontal swipe. The number '8' was arrived
|
||||
// at by trial and error.
|
||||
if ((deltaX == 0) || (fabs(deltaX) <= fabs(deltaY) * 8)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// geckoEvent must be initialized now (while anEvent is still available),
|
||||
// but we also need to access it (and modify it) in the following "block"
|
||||
// (the trackingHandler passed to [NSEvent trackSwipeEventWithOptions:...]).
|
||||
// Normally we'd give it the '__block' keyword, but this makes the compiler
|
||||
// crash :-( Without the '__block' keyword, it becomes immutable from
|
||||
// trackingHandler. So trackingHandler must make a copy of it and modify
|
||||
// that.
|
||||
nsSimpleGestureEvent geckoEvent(PR_TRUE, NS_SIMPLE_GESTURE_SWIPE, mGeckoChild, 0, 0.0);
|
||||
[self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
|
||||
|
||||
__block BOOL animationCancelled = NO;
|
||||
// At this point, anEvent is the first scroll wheel event in a two-finger
|
||||
// horizontal gesture that we've decided to treat as a swipe. When we call
|
||||
// [NSEvent trackSwipeEventWithOptions:...], the OS interprets all
|
||||
// subsequent scroll wheel events that are part of this gesture as a swipe,
|
||||
// and stops sending them to us. The OS calls the trackingHandler "block"
|
||||
// multiple times, asynchronously (sometimes after [NSEvent
|
||||
// maybeTrackScrollEventAsSwipe:...] has returned). The OS determines when
|
||||
// the gesture has finished, and whether or not it was "successful" -- this
|
||||
// information is passed to trackingHandler. We must be careful to only
|
||||
// call [NSEvent maybeTrackScrollEventAsSwipe:...] on a "real" swipe --
|
||||
// otherwise two-finger scrolling performance will suffer significantly.
|
||||
[anEvent trackSwipeEventWithOptions:0
|
||||
dampenAmountThresholdMin:-1
|
||||
max:1
|
||||
usingHandler:^(CGFloat gestureAmount, NSEventPhase phase, BOOL isComplete, BOOL *stop) {
|
||||
if (animationCancelled) {
|
||||
*stop = YES;
|
||||
return;
|
||||
}
|
||||
if (isComplete) {
|
||||
if (gestureAmount) {
|
||||
nsSimpleGestureEvent geckoEventCopy(geckoEvent);
|
||||
if (gestureAmount > 0) {
|
||||
geckoEventCopy.direction |= nsIDOMSimpleGestureEvent::DIRECTION_LEFT;
|
||||
} else {
|
||||
geckoEventCopy.direction |= nsIDOMSimpleGestureEvent::DIRECTION_RIGHT;
|
||||
}
|
||||
mGeckoChild->DispatchWindowEvent(geckoEventCopy);
|
||||
}
|
||||
mSwipeAnimationCancelled = nil;
|
||||
}
|
||||
}];
|
||||
|
||||
// We keep a pointer to the __block variable (animationCanceled) so we
|
||||
// can cancel our block handler at any time. Note: We must assign
|
||||
// &animationCanceled after our block creation and copy -- its address
|
||||
// isn't resolved until then!
|
||||
mSwipeAnimationCancelled = &animationCancelled;
|
||||
}
|
||||
#endif // #ifdef __LP64__
|
||||
|
||||
// Returning NO from this method only disallows ordering on mousedown - in order
|
||||
// to prevent it for mouseup too, we need to call [NSApp preventWindowOrdering]
|
||||
// when handling the mousedown event.
|
||||
@ -3670,6 +3795,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
geckoEvent.delta = NSToIntRound(scrollDeltaPixels);
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
mGeckoChild->DispatchWindowEvent(geckoEvent);
|
||||
#ifdef __LP64__
|
||||
// scrollOverflow tells us when the user has tried to scroll past the edge
|
||||
// of a page (in those cases it's non-zero). Gecko only sets it when
|
||||
// processing NS_MOUSE_PIXEL_SCROLL events (not MS_MOUSE_SCROLL events).
|
||||
// It only means left/right overflow when Gecko is processing a horizontal
|
||||
// event.
|
||||
if (inAxis & nsMouseScrollEvent::kIsHorizontal) {
|
||||
[self maybeTrackScrollEventAsSwipe:theEvent
|
||||
scrollOverflow:geckoEvent.scrollOverflow];
|
||||
}
|
||||
#endif // #ifdef __LP64__
|
||||
}
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
|
Loading…
Reference in New Issue
Block a user