Index: chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm |
diff --git a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm |
index 47f795edf3d55dcf6cecc0e88c1599affdcd062e..f147bd5542d7a75ddc21926f9b766de58a8849e1 100644 |
--- a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm |
+++ b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm |
@@ -4,18 +4,46 @@ |
#import "chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.h" |
+#import "base/mac/sdk_forward_declarations.h" |
#include "chrome/browser/ui/browser.h" |
#include "chrome/browser/ui/browser_commands.h" |
#include "chrome/browser/ui/browser_finder.h" |
- |
-#import "base/mac/sdk_forward_declarations.h" |
#import "chrome/browser/ui/cocoa/history_overlay_controller.h" |
+#include "third_party/WebKit/public/web/WebInputEvent.h" |
// Once we call `[NSEvent trackSwipeEventWithOptions:]`, we cannot reliably |
// expect NSTouch callbacks. We set this variable to YES and ignore NSTouch |
// callbacks. |
static BOOL forceMagicMouse = NO; |
Avi (use Gerrit)
2014/05/29 15:02:01
Move this inside the anon namespace below and drop
erikchen
2014/05/29 20:42:38
Done.
|
+namespace { |
+// The horizontal distance required to cause the browser to perform a history |
+// navigation. |
+const CGFloat kHistorySwipeThreshold = 0.08; |
+// The horizontal distance required for this class to start consuming events, |
+// which stops the events from reaching the renderer. |
+const CGFloat kConsumeEventThreshold = 0.01; |
+// If there has been sufficient vertical motion, the gesture can't be intended |
+// for history swiping. |
+const CGFloat kCancelEventVerticalThreshold = 0.24; |
+// If there has been sufficient vertical motion, and more vertical than |
+// horizontal motion, the gesture can't be intended for history swiping. |
+const CGFloat kCancelEventVerticalLowerThreshold = 0.01; |
+} // namespace |
Avi (use Gerrit)
2014/05/29 15:02:01
CrOS has a history swipe, and I'm curious as to ho
erikchen
2014/05/29 20:42:38
Not really. :( Though the infrastructure is genera
Avi (use Gerrit)
2014/05/29 20:48:56
Gotcha.
Good to hear that you're going to be unif
|
+ |
+@interface HistorySwiper () |
+// Given a touch event, returns the average touch position. |
+- (NSPoint)averagePositionInEvent:(NSEvent*)event; |
+// Updates internal state with the location information from the touch event. |
+- (void)updateGestureCurrentPointFromEvent:(NSEvent*)event; |
+// Updates the state machine with the given touch event. |
+// Returns NO if no further processing of the event should happen. |
+- (BOOL)processTouchEventForHistorySwiping:(NSEvent*)event; |
+// Returns whether the wheel event should be consumed, and not passed to the |
+// renderer. |
+- (BOOL)shouldConsumeWheelEvent:(NSEvent*)event; |
+@end |
Avi (use Gerrit)
2014/05/29 15:02:01
For this and the anon namespace above, put blank l
erikchen
2014/05/29 20:42:38
Done.
|
+ |
@implementation HistorySwiper |
@synthesize delegate = delegate_; |
@@ -43,17 +71,11 @@ static BOOL forceMagicMouse = NO; |
return NO; |
} |
-- (void)gotUnhandledWheelEvent { |
- gotUnhandledWheelEvent_ = YES; |
-} |
- |
-- (void)scrollOffsetPinnedToLeft:(BOOL)left toRight:(BOOL)right { |
- isPinnedLeft_ = left; |
- isPinnedRight_ = right; |
-} |
- |
-- (void)setHasHorizontalScrollbar:(BOOL)hasHorizontalScrollbar { |
- hasHorizontalScrollbar_ = hasHorizontalScrollbar; |
+- (void)rendererHandledWheelEvent:(const blink::WebMouseWheelEvent&)event |
+ consumed:(BOOL)consumed { |
+ if (event.phase != NSEventPhaseBegan) |
+ return; |
+ beganEventUnconsumed_ = !consumed; |
} |
- (BOOL)canRubberbandLeft:(NSView*)view { |
@@ -66,7 +88,7 @@ static BOOL forceMagicMouse = NO; |
// History swiping is possible. By default, disallow rubberbanding. If the |
// user has both started, and then cancelled history swiping for this |
// gesture, allow rubberbanding. |
- return inGesture_ && historySwipeCancelled_; |
+ return inGesture_ && recognitionState_ == history_swiper::kCancelled; |
} |
- (BOOL)canRubberbandRight:(NSView*)view { |
@@ -79,7 +101,7 @@ static BOOL forceMagicMouse = NO; |
// History swiping is possible. By default, disallow rubberbanding. If the |
// user has both started, and then cancelled history swiping for this |
// gesture, allow rubberbanding. |
- return inGesture_ && historySwipeCancelled_; |
+ return inGesture_ && recognitionState_ == history_swiper::kCancelled; |
} |
// Is is theoretically possible for multiple simultaneous gestures to occur, if |
@@ -91,11 +113,11 @@ static BOOL forceMagicMouse = NO; |
inGesture_ = YES; |
++currentGestureId_; |
// Reset state pertaining to previous gestures. |
- historySwipeCancelled_ = NO; |
gestureStartPointValid_ = NO; |
- gotUnhandledWheelEvent_ = NO; |
receivedTouch_ = NO; |
mouseScrollDelta_ = NSZeroSize; |
+ beganEventUnconsumed_ = NO; |
+ recognitionState_ = history_swiper::kPending; |
} |
- (void)endGestureWithEvent:(NSEvent*)event { |
@@ -147,75 +169,104 @@ static BOOL forceMagicMouse = NO; |
} |
- (void)touchesMovedWithEvent:(NSEvent*)event { |
- receivedTouch_ = YES; |
- if (![self shouldProcessEventForHistorySwiping:event]) |
- return; |
- |
- [self updateGestureCurrentPointFromEvent:event]; |
- |
- if (historyOverlay_) { |
- // Consider cancelling the history swipe gesture. |
- if ([self shouldCancelHorizontalSwipeWithCurrentPoint:gestureCurrentPoint_ |
- startPoint:gestureStartPoint_]) { |
- [self cancelHistorySwipe]; |
- return; |
- } |
- |
- [self updateProgressBar]; |
- } |
+ [self processTouchEventForHistorySwiping:event]; |
} |
+ |
- (void)touchesCancelledWithEvent:(NSEvent*)event { |
- receivedTouch_ = YES; |
- if (![self shouldProcessEventForHistorySwiping:event]) |
+ if (![self processTouchEventForHistorySwiping:event]) |
return; |
- if (historyOverlay_) |
- [self cancelHistorySwipe]; |
+ [self cancelHistorySwipe]; |
} |
+ |
- (void)touchesEndedWithEvent:(NSEvent*)event { |
- receivedTouch_ = YES; |
- if (![self shouldProcessEventForHistorySwiping:event]) |
+ if (![self processTouchEventForHistorySwiping:event]) |
return; |
- [self updateGestureCurrentPointFromEvent:event]; |
if (historyOverlay_) { |
BOOL finished = [self updateProgressBar]; |
// If the gesture was completed, perform a navigation. |
- if (finished) { |
+ if (finished) |
[self navigateBrowserInDirection:historySwipeDirection_]; |
- } |
// Remove the history overlay. |
[self endHistorySwipe]; |
+ // The gesture was completed. |
+ recognitionState_ = history_swiper::kCompleted; |
} |
} |
-- (BOOL)shouldProcessEventForHistorySwiping:(NSEvent*)event { |
- // TODO(erikchen): what is the point of NSEventTypeSwipe and NSScrollWheel? |
+- (BOOL)processTouchEventForHistorySwiping:(NSEvent*)event { |
+ receivedTouch_ = YES; |
+ |
NSEventType type = [event type]; |
- return type == NSEventTypeBeginGesture || type == NSEventTypeEndGesture || |
- type == NSEventTypeGesture; |
+ if (type != NSEventTypeBeginGesture && type != NSEventTypeEndGesture && |
+ type != NSEventTypeGesture) { |
+ return NO; |
+ } |
+ |
+ switch (recognitionState_) { |
+ case history_swiper::kCancelled: |
+ case history_swiper::kCompleted: |
+ return NO; |
+ case history_swiper::kPending: |
+ [self updateGestureCurrentPointFromEvent:event]; |
+ return NO; |
+ case history_swiper::kPotential: |
+ case history_swiper::kTracking: |
+ break; |
+ } |
+ |
+ [self updateGestureCurrentPointFromEvent:event]; |
+ |
+ // Consider cancelling the history swipe gesture. |
+ if ([self shouldCancelHorizontalSwipeWithCurrentPoint:gestureCurrentPoint_ |
+ startPoint:gestureStartPoint_]) { |
+ [self cancelHistorySwipe]; |
+ return NO; |
+ } |
+ |
+ if (recognitionState_ == history_swiper::kPotential) { |
+ // The user is in the process of doing history swiping. If the history |
+ // swipe has progressed sufficiently far, stop sending events to the |
+ // renderer. |
+ BOOL sufficientlyFar = fabs(gestureCurrentPoint_.x - gestureStartPoint_.x) > |
+ kConsumeEventThreshold; |
+ if (sufficientlyFar) |
+ recognitionState_ = history_swiper::kTracking; |
+ } |
+ |
+ if (historyOverlay_) |
+ [self updateProgressBar]; |
+ return YES; |
} |
// Consider cancelling the horizontal swipe if the user was intending a |
// vertical swipe. |
- (BOOL)shouldCancelHorizontalSwipeWithCurrentPoint:(NSPoint)currentPoint |
startPoint:(NSPoint)startPoint { |
- // There's been more vertical distance than horizontal distance. |
CGFloat yDelta = fabs(currentPoint.y - startPoint.y); |
CGFloat xDelta = fabs(currentPoint.x - startPoint.x); |
- BOOL moreVertThanHoriz = yDelta > xDelta && yDelta > 0.1; |
+ |
+ // The gesture is pretty clearly more vertical than horizontal. |
+ if (yDelta > 2 * xDelta) |
+ return YES; |
+ |
+ // There's been more vertical distance than horizontal distance. |
+ if (yDelta * 1.3 > xDelta && yDelta > kCancelEventVerticalLowerThreshold) |
+ return YES; |
// There's been a lot of vertical distance. |
- BOOL muchVert = yDelta > 0.32; |
+ if (yDelta > kCancelEventVerticalThreshold) |
+ return YES; |
- return moreVertThanHoriz || muchVert; |
+ return NO; |
} |
- (void)cancelHistorySwipe { |
[self endHistorySwipe]; |
- historySwipeCancelled_ = YES; |
+ recognitionState_ = history_swiper::kCancelled; |
} |
- (void)endHistorySwipe { |
@@ -232,9 +283,7 @@ static BOOL forceMagicMouse = NO; |
float progress = 0; |
BOOL finished = NO; |
- // This value was determined by experimentation. |
- CGFloat requiredSwipeDistance = 0.08; |
- progress = (currentPoint.x - startPoint.x) / requiredSwipeDistance; |
+ progress = (currentPoint.x - startPoint.x) / kHistorySwipeThreshold; |
// If the swipe is a backwards gesture, we need to invert progress. |
if (historySwipeDirection_ == history_swiper::kBackwards) |
progress *= -1; |
@@ -343,14 +392,6 @@ static BOOL forceMagicMouse = NO; |
if (!browserCanMove) |
return NO; |
- if (isRightScroll) { |
- if (hasHorizontalScrollbar_ && !isPinnedRight_) |
- return NO; |
- } else { |
- if (hasHorizontalScrollbar_ && !isPinnedLeft_) |
- return NO; |
- } |
- |
[self initiateMagicMouseHistorySwipe:isRightScroll event:theEvent]; |
return YES; |
} |
@@ -456,30 +497,24 @@ static BOOL forceMagicMouse = NO; |
if (![theEvent respondsToSelector:@selector(phase)]) |
return NO; |
- // Check for regular mouse wheel scroll events. |
- if ([theEvent phase] == NSEventPhaseNone && |
- [theEvent momentumPhase] == NSEventPhaseNone) { |
+ // The only events that this class consumes have type NSEventPhaseChanged. |
+ // This simultaneously weeds our regular mouse wheel scroll events, and |
+ // gesture events with incorrect phase. |
+ if ([theEvent phase] != NSEventPhaseChanged && |
+ [theEvent momentumPhase] != NSEventPhaseChanged) { |
return NO; |
} |
// We've already processed this gesture. |
- if (lastProcessedGestureId_ == currentGestureId_) { |
- // A new event may come in before it's recognized as a gesture. |
- // We have not yet reset the state from the last gesture. |
- // Let it pass through. |
- if ([theEvent phase] == NSEventPhaseBegan || |
- [theEvent phase] == NSEventPhaseMayBegin) { |
- return NO; |
- } |
- |
- // The user cancelled the history swiper. Ignore all events. |
- if (historySwipeCancelled_) |
- return NO; |
- |
- // The user completed the history swiper. Swallow all events. |
- return YES; |
+ if (lastProcessedGestureId_ == currentGestureId_ && |
+ recognitionState_ != history_swiper::kPending) { |
+ return [self shouldConsumeWheelEvent:theEvent]; |
} |
+ // Don't allow momentum events to start history swiping. |
+ if ([theEvent momentumPhase] != NSEventPhaseNone) |
+ return NO; |
+ |
BOOL systemSettingsValid = [self systemSettingsAllowHistorySwiping:theEvent]; |
if (!systemSettingsValid) |
return NO; |
@@ -487,52 +522,24 @@ static BOOL forceMagicMouse = NO; |
if (![delegate_ shouldAllowHistorySwiping]) |
return NO; |
- // Don't even consider enabling history swiping until blink has decided it is |
- // not going to handle the event. |
- if (!gotUnhandledWheelEvent_) |
- return NO; |
- |
- // If the window has a horizontal scroll bar, sometimes Cocoa gets confused |
- // and sends us momentum scroll wheel events instead of gesture scroll events |
- // (even though the user is still actively swiping). |
- if ([theEvent phase] != NSEventPhaseChanged && |
- [theEvent momentumPhase] != NSEventPhaseChanged) { |
+ // Don't enable history swiping until the renderer has decided to not consume |
+ // the event with phase NSEventPhaseBegan. |
+ if (!beganEventUnconsumed_) |
return NO; |
- } |
if (!inGesture_) |
return NO; |
- if (!receivedTouch_ || forceMagicMouse) { |
+ if (!receivedTouch_ || forceMagicMouse) |
return [self maybeHandleMagicMouseHistorySwiping:theEvent]; |
- } |
- CGFloat yDelta = gestureCurrentPoint_.y - gestureStartPoint_.y; |
CGFloat xDelta = gestureCurrentPoint_.x - gestureStartPoint_.x; |
- // Require the user's gesture to have moved more than a minimal amount. |
- if (fabs(xDelta) < 0.01) |
- return NO; |
- |
- // Require the user's gesture to be slightly more horizontal than vertical. |
- BOOL isHorizontalGesture = fabs(xDelta) > 1.3 * fabs(yDelta); |
- |
- if (!isHorizontalGesture) |
- return NO; |
- |
BOOL isRightScroll = xDelta > 0; |
BOOL inverted = [self isEventDirectionInverted:theEvent]; |
if (inverted) |
isRightScroll = !isRightScroll; |
- if (isRightScroll) { |
- if (hasHorizontalScrollbar_ && !isPinnedRight_) |
- return NO; |
- } else { |
- if (hasHorizontalScrollbar_ && !isPinnedLeft_) |
- return NO; |
- } |
- |
history_swiper::NavigationDirection direction = |
isRightScroll ? history_swiper::kForwards : history_swiper::kBackwards; |
BOOL browserCanMove = |
@@ -542,7 +549,24 @@ static BOOL forceMagicMouse = NO; |
lastProcessedGestureId_ = currentGestureId_; |
[self beginHistorySwipeInDirection:direction event:theEvent]; |
- return YES; |
+ recognitionState_ = history_swiper::kPotential; |
+ return [self shouldConsumeWheelEvent:theEvent]; |
+} |
+ |
+- (BOOL)shouldConsumeWheelEvent:(NSEvent*)event { |
+ switch (recognitionState_) { |
+ case history_swiper::kPending: |
+ case history_swiper::kCancelled: |
+ return NO; |
+ case history_swiper::kTracking: |
+ case history_swiper::kCompleted: |
+ return YES; |
+ case history_swiper::kPotential: |
+ // It is unclear whether the user is attempting to perform history |
+ // swiping. If the event has a vertical component, send it on to the |
+ // renderer. |
+ return event.scrollingDeltaY == 0; |
+ } |
} |
@end |