diff options
-rw-r--r-- | chrome/browser/cocoa/tab_controller.h | 6 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_controller.mm | 10 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_controller.h | 5 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_controller.mm | 64 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_view.mm | 42 |
5 files changed, 110 insertions, 17 deletions
diff --git a/chrome/browser/cocoa/tab_controller.h b/chrome/browser/cocoa/tab_controller.h index 565c95c..316b985 100644 --- a/chrome/browser/cocoa/tab_controller.h +++ b/chrome/browser/cocoa/tab_controller.h @@ -76,6 +76,12 @@ enum TabLoadingState { // (Re)apply the current theme. - (void)applyTheme; + +// Called by the tabs to determine whether we are in rapid (tab) closure mode. +// In this mode, we handle clicks slightly differently due to animation. +// Ideally, tabs would know about their own animation and wouldn't need this. +- (BOOL)inRapidClosureMode; + @end @interface TabController(TestingAPI) diff --git a/chrome/browser/cocoa/tab_controller.mm b/chrome/browser/cocoa/tab_controller.mm index 96d7e5d..75f860d 100644 --- a/chrome/browser/cocoa/tab_controller.mm +++ b/chrome/browser/cocoa/tab_controller.mm @@ -182,4 +182,14 @@ [titleView_ setTextColor:color ? color : [NSColor textColor]]; [[self view] setNeedsDisplay:YES]; } + +// Called by the tabs to determine whether we are in rapid (tab) closure mode. +- (BOOL)inRapidClosureMode { + if ([[self target] respondsToSelector:@selector(inRapidClosureMode)]) { + return [[self target] performSelector:@selector(inRapidClosureMode)] ? + YES : NO; + } + return NO; +} + @end diff --git a/chrome/browser/cocoa/tab_strip_controller.h b/chrome/browser/cocoa/tab_strip_controller.h index 37524b5..e1778b5 100644 --- a/chrome/browser/cocoa/tab_strip_controller.h +++ b/chrome/browser/cocoa/tab_strip_controller.h @@ -130,6 +130,11 @@ class ToolbarModel; // The user changed the theme, or theme state changed. - (void)applyTheme; +// Are we in rapid (tab) closure mode? I.e., is a full layout deferred (while +// the user closes tabs)? Needed to overcome missing clicks during rapid tab +// closure. +- (BOOL)inRapidClosureMode; + // Default height for tabs. + (CGFloat)defaultTabHeight; diff --git a/chrome/browser/cocoa/tab_strip_controller.mm b/chrome/browser/cocoa/tab_strip_controller.mm index fb39bd7..c6a42f7 100644 --- a/chrome/browser/cocoa/tab_strip_controller.mm +++ b/chrome/browser/cocoa/tab_strip_controller.mm @@ -37,17 +37,52 @@ NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged"; // view. static const float kUseFullAvailableWidth = -1.0; -// A simple view class that prevents the windowserver from dragging the -// area behind tabs. Sometimes core animation confuses it. -@interface TabStripControllerDragBlockingView : NSView +// A simple view class that prevents the Window Server from dragging the area +// behind tabs. Sometimes core animation confuses it. Unfortunately, it can also +// falsely pick up clicks during rapid tab closure, so we have to account for +// that. +@interface TabStripControllerDragBlockingView : NSView { + TabStripController* controller_; // weak; owns us +} + +- (id)initWithFrame:(NSRect)frameRect + controller:(TabStripController*)controller; @end @implementation TabStripControllerDragBlockingView - (BOOL)mouseDownCanMoveWindow {return NO;} - (void)drawRect:(NSRect)rect {} + +- (id)initWithFrame:(NSRect)frameRect + controller:(TabStripController*)controller { + if ((self = [super initWithFrame:frameRect])) + controller_ = controller; + return self; +} + +// In "rapid tab closure" mode (i.e., the user is clicking close tab buttons in +// rapid succession), the animations confuse Cocoa's hit testing (which appears +// to use cached results, among other tricks), so this view can somehow end up +// getting a mouse down event. Thus we do an explicit hit test during rapid tab +// closure, and if we find that we got a mouse down we shouldn't have, we send +// it off to the appropriate view. +- (void)mouseDown:(NSEvent*)event { + if ([controller_ inRapidClosureMode]) { + NSView* superview = [self superview]; + NSPoint hitLocation = + [[superview superview] convertPoint:[event locationInWindow] + fromView:nil]; + NSView* hitView = [superview hitTest:hitLocation]; + if (hitView != self) { + [hitView mouseDown:event]; + return; + } + } + [super mouseDown:event]; +} @end @interface TabStripController(Private) -- (BOOL)useFullWidthForLayout; +- (void)installTrackingArea; - (void)addSubviewToPermanentList:(NSView*)aView; - (void)regenerateSubviewList; - (NSInteger)indexForContentsView:(NSView*)view; @@ -82,8 +117,9 @@ static const float kUseFullAvailableWidth = -1.0; [newTabButton_ setAction:@selector(commandDispatch:)]; [newTabButton_ setTag:IDC_NEW_TAB]; targetFrames_.reset([[NSMutableDictionary alloc] init]); - dragBlockingView_.reset([[TabStripControllerDragBlockingView alloc] - initWithFrame:NSZeroRect]); + dragBlockingView_.reset( + [[TabStripControllerDragBlockingView alloc] initWithFrame:NSZeroRect + controller:self]); [self addSubviewToPermanentList:dragBlockingView_]; newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0); availableResizeWidth_ = kUseFullAvailableWidth; @@ -321,11 +357,11 @@ static const float kUseFullAvailableWidth = -1.0; // may not be able to use the entire width if the user is quickly closing // tabs. float availableWidth = 0; - if ([self useFullWidthForLayout]) { + if ([self inRapidClosureMode]) { + availableWidth = availableResizeWidth_; + } else { availableWidth = NSWidth([tabView_ frame]); availableWidth -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset; - } else { - availableWidth = availableResizeWidth_; } availableWidth -= kIndentLeavingSpaceForControls; @@ -418,11 +454,11 @@ static const float kUseFullAvailableWidth = -1.0; [newTabButton_ setHidden:YES]; } else { NSRect newTabNewFrame = [newTabButton_ frame]; - if ([self useFullWidthForLayout]) + if ([self inRapidClosureMode]) + newTabNewFrame.origin = NSMakePoint(offset + kNewTabButtonOffset, 0); + else newTabNewFrame.origin = NSMakePoint(MIN(availableWidth, offset + kNewTabButtonOffset), 0); - else - newTabNewFrame.origin = NSMakePoint(offset + kNewTabButtonOffset, 0); newTabNewFrame.origin.x = MAX(newTabNewFrame.origin.x, NSMaxX(placeholderFrame_)); if (i > 0 && [newTabButton_ isHidden]) { @@ -776,8 +812,8 @@ static const float kUseFullAvailableWidth = -1.0; [self layoutTabsWithAnimation:NO regenerateSubviews:NO]; } -- (BOOL)useFullWidthForLayout { - return availableResizeWidth_ == kUseFullAvailableWidth; +- (BOOL)inRapidClosureMode { + return availableResizeWidth_ != kUseFullAvailableWidth; } - (void)mouseMoved:(NSEvent *)event { diff --git a/chrome/browser/cocoa/tab_view.mm b/chrome/browser/cocoa/tab_view.mm index 16074e9..351882c 100644 --- a/chrome/browser/cocoa/tab_view.mm +++ b/chrome/browser/cocoa/tab_view.mm @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/browser/cocoa/tab_view.h" + #include "chrome/browser/cocoa/nsimage_cache.h" #include "chrome/browser/cocoa/tab_controller.h" -#include "chrome/browser/cocoa/tab_view.h" #include "chrome/browser/cocoa/tab_window_controller.h" - // Constants for inset and control points for tab shape. static const CGFloat kInsetMultiplier = 2.0/3.0; static const CGFloat kControlPoint1Multiplier = 1.0/3.0; @@ -150,9 +150,27 @@ static const NSTimeInterval kAnimationHideDuration = 0.4; static const CGFloat kTearDistance = 36.0; static const NSTimeInterval kTearDuration = 0.333; -static const double kDragStartDistance = 3.0; + +// This is used to judge whether the mouse has moved during rapid closure; if it +// has moved less than the threshold, we want to close the tab. +static const CGFloat kRapidCloseDist = 2.5; - (void)mouseDown:(NSEvent *)theEvent { + NSPoint downLocation = [theEvent locationInWindow]; + + // During the tab closure animation (in particular, during rapid tab closure), + // we may get incorrectly hit with a mouse down. If it should have gone to the + // close button, we send it there -- it should then track the mouse, so we + // don't have to worry about mouse ups. + if ([controller_ inRapidClosureMode]) { + NSPoint hitLocation = [[self superview] convertPoint:downLocation + fromView:nil]; + if ([self hitTest:hitLocation] == closeButton_) { + [closeButton_ mouseDown:theEvent]; + return; + } + } + // Fire the action to select the tab. if ([[controller_ target] respondsToSelector:[controller_ action]]) [[controller_ target] performSelector:[controller_ action] @@ -198,6 +216,24 @@ static const double kDragStartDistance = 3.0; if (type == NSLeftMouseDragged) { [self mouseDragged:theEvent]; } else { // Mouse Up + NSPoint upLocation = [theEvent locationInWindow]; + CGFloat dx = upLocation.x - downLocation.x; + CGFloat dy = upLocation.y - downLocation.y; + + // During rapid tab closure (mashing tab close buttons), we may get hit + // with a mouse down. As long as the mouse up is over the close button, + // and the mouse hasn't moved too much, we close the tab. + if ((dx*dx + dy*dy) <= kRapidCloseDist*kRapidCloseDist && + [controller_ inRapidClosureMode]) { + NSPoint hitLocation = + [[self superview] convertPoint:[theEvent locationInWindow] + fromView:nil]; + if ([self hitTest:hitLocation] == closeButton_) { + [controller_ closeTab:self]; + break; + } + } + [self mouseUp:theEvent]; break; } |