summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/cocoa/tab_controller.h6
-rw-r--r--chrome/browser/cocoa/tab_controller.mm10
-rw-r--r--chrome/browser/cocoa/tab_strip_controller.h5
-rw-r--r--chrome/browser/cocoa/tab_strip_controller.mm64
-rw-r--r--chrome/browser/cocoa/tab_view.mm42
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;
}