| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "chrome/browser/cocoa/tab_strip_controller.h" | 5 #import "chrome/browser/cocoa/tab_strip_controller.h" |
| 6 | 6 |
| 7 #include "app/l10n_util.h" | 7 #include "app/l10n_util.h" |
| 8 #include "base/mac_util.h" | 8 #include "base/mac_util.h" |
| 9 #include "base/sys_string_conversions.h" | 9 #include "base/sys_string_conversions.h" |
| 10 #include "chrome/app/chrome_dll_resource.h" | 10 #include "chrome/app/chrome_dll_resource.h" |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 #include "chrome/browser/tabs/tab_strip_model.h" | 30 #include "chrome/browser/tabs/tab_strip_model.h" |
| 31 #include "grit/generated_resources.h" | 31 #include "grit/generated_resources.h" |
| 32 #include "skia/ext/skia_utils_mac.h" | 32 #include "skia/ext/skia_utils_mac.h" |
| 33 | 33 |
| 34 NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged"; | 34 NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged"; |
| 35 | 35 |
| 36 // A value to indicate tab layout should use the full available width of the | 36 // A value to indicate tab layout should use the full available width of the |
| 37 // view. | 37 // view. |
| 38 static const float kUseFullAvailableWidth = -1.0; | 38 static const float kUseFullAvailableWidth = -1.0; |
| 39 | 39 |
| 40 // A simple view class that prevents the windowserver from dragging the | 40 // A simple view class that prevents the Window Server from dragging the area |
| 41 // area behind tabs. Sometimes core animation confuses it. | 41 // behind tabs. Sometimes core animation confuses it. Unfortunately, it can also |
| 42 @interface TabStripControllerDragBlockingView : NSView | 42 // falsely pick up clicks during rapid tab closure, so we have to account for |
| 43 // that. |
| 44 @interface TabStripControllerDragBlockingView : NSView { |
| 45 TabStripController* controller_; // weak; owns us |
| 46 } |
| 47 |
| 48 - (id)initWithFrame:(NSRect)frameRect |
| 49 controller:(TabStripController*)controller; |
| 43 @end | 50 @end |
| 44 @implementation TabStripControllerDragBlockingView | 51 @implementation TabStripControllerDragBlockingView |
| 45 - (BOOL)mouseDownCanMoveWindow {return NO;} | 52 - (BOOL)mouseDownCanMoveWindow {return NO;} |
| 46 - (void)drawRect:(NSRect)rect {} | 53 - (void)drawRect:(NSRect)rect {} |
| 54 |
| 55 - (id)initWithFrame:(NSRect)frameRect |
| 56 controller:(TabStripController*)controller { |
| 57 if ((self = [super initWithFrame:frameRect])) |
| 58 controller_ = controller; |
| 59 return self; |
| 60 } |
| 61 |
| 62 // In "rapid tab closure" mode (i.e., the user is clicking close tab buttons in |
| 63 // rapid succession), the animations confuse Cocoa's hit testing (which appears |
| 64 // to use cached results, among other tricks), so this view can somehow end up |
| 65 // getting a mouse down event. Thus we do an explicit hit test during rapid tab |
| 66 // closure, and if we find that we got a mouse down we shouldn't have, we send |
| 67 // it off to the appropriate view. |
| 68 - (void)mouseDown:(NSEvent*)event { |
| 69 if ([controller_ inRapidClosureMode]) { |
| 70 NSView* superview = [self superview]; |
| 71 NSPoint hitLocation = |
| 72 [[superview superview] convertPoint:[event locationInWindow] |
| 73 fromView:nil]; |
| 74 NSView* hitView = [superview hitTest:hitLocation]; |
| 75 if (hitView != self) { |
| 76 [hitView mouseDown:event]; |
| 77 return; |
| 78 } |
| 79 } |
| 80 [super mouseDown:event]; |
| 81 } |
| 47 @end | 82 @end |
| 48 | 83 |
| 49 @interface TabStripController(Private) | 84 @interface TabStripController(Private) |
| 50 - (BOOL)useFullWidthForLayout; | 85 - (void)installTrackingArea; |
| 51 - (void)addSubviewToPermanentList:(NSView*)aView; | 86 - (void)addSubviewToPermanentList:(NSView*)aView; |
| 52 - (void)regenerateSubviewList; | 87 - (void)regenerateSubviewList; |
| 53 - (NSInteger)indexForContentsView:(NSView*)view; | 88 - (NSInteger)indexForContentsView:(NSView*)view; |
| 54 - (void)updateFavIconForContents:(TabContents*)contents | 89 - (void)updateFavIconForContents:(TabContents*)contents |
| 55 atIndex:(NSInteger)index; | 90 atIndex:(NSInteger)index; |
| 56 @end | 91 @end |
| 57 | 92 |
| 58 @implementation TabStripController | 93 @implementation TabStripController |
| 59 | 94 |
| 60 - (id)initWithView:(TabStripView*)view | 95 - (id)initWithView:(TabStripView*)view |
| (...skipping 14 matching lines...) Expand all Loading... |
| 75 // some reason, if the view is present in the nib apriori, it draws | 110 // some reason, if the view is present in the nib apriori, it draws |
| 76 // correctly. If we create it in code and add it to the tab view, it draws | 111 // correctly. If we create it in code and add it to the tab view, it draws |
| 77 // with all sorts of crazy artifacts. | 112 // with all sorts of crazy artifacts. |
| 78 newTabButton_ = [[tabView_ subviews] objectAtIndex:0]; | 113 newTabButton_ = [[tabView_ subviews] objectAtIndex:0]; |
| 79 DCHECK([newTabButton_ isKindOfClass:[NSButton class]]); | 114 DCHECK([newTabButton_ isKindOfClass:[NSButton class]]); |
| 80 [self addSubviewToPermanentList:newTabButton_]; | 115 [self addSubviewToPermanentList:newTabButton_]; |
| 81 [newTabButton_ setTarget:nil]; | 116 [newTabButton_ setTarget:nil]; |
| 82 [newTabButton_ setAction:@selector(commandDispatch:)]; | 117 [newTabButton_ setAction:@selector(commandDispatch:)]; |
| 83 [newTabButton_ setTag:IDC_NEW_TAB]; | 118 [newTabButton_ setTag:IDC_NEW_TAB]; |
| 84 targetFrames_.reset([[NSMutableDictionary alloc] init]); | 119 targetFrames_.reset([[NSMutableDictionary alloc] init]); |
| 85 dragBlockingView_.reset([[TabStripControllerDragBlockingView alloc] | 120 dragBlockingView_.reset( |
| 86 initWithFrame:NSZeroRect]); | 121 [[TabStripControllerDragBlockingView alloc] initWithFrame:NSZeroRect |
| 122 controller:self]); |
| 87 [self addSubviewToPermanentList:dragBlockingView_]; | 123 [self addSubviewToPermanentList:dragBlockingView_]; |
| 88 newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0); | 124 newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0); |
| 89 availableResizeWidth_ = kUseFullAvailableWidth; | 125 availableResizeWidth_ = kUseFullAvailableWidth; |
| 90 | 126 |
| 91 // Install the permanent subviews. | 127 // Install the permanent subviews. |
| 92 [self regenerateSubviewList]; | 128 [self regenerateSubviewList]; |
| 93 | 129 |
| 94 // Watch for notifications that the tab strip view has changed size so | 130 // Watch for notifications that the tab strip view has changed size so |
| 95 // we can tell it to layout for the new size. | 131 // we can tell it to layout for the new size. |
| 96 [[NSNotificationCenter defaultCenter] | 132 [[NSNotificationCenter defaultCenter] |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 314 [[NSAnimationContext currentContext] setDuration:0.2]; | 350 [[NSAnimationContext currentContext] setDuration:0.2]; |
| 315 | 351 |
| 316 // Update the current subviews and their z-order if requested. | 352 // Update the current subviews and their z-order if requested. |
| 317 if (doUpdate) | 353 if (doUpdate) |
| 318 [self regenerateSubviewList]; | 354 [self regenerateSubviewList]; |
| 319 | 355 |
| 320 // Compute the base width of tabs given how much room we're allowed. We | 356 // Compute the base width of tabs given how much room we're allowed. We |
| 321 // may not be able to use the entire width if the user is quickly closing | 357 // may not be able to use the entire width if the user is quickly closing |
| 322 // tabs. | 358 // tabs. |
| 323 float availableWidth = 0; | 359 float availableWidth = 0; |
| 324 if ([self useFullWidthForLayout]) { | 360 if ([self inRapidClosureMode]) { |
| 361 availableWidth = availableResizeWidth_; |
| 362 } else { |
| 325 availableWidth = NSWidth([tabView_ frame]); | 363 availableWidth = NSWidth([tabView_ frame]); |
| 326 availableWidth -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset; | 364 availableWidth -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset; |
| 327 } else { | |
| 328 availableWidth = availableResizeWidth_; | |
| 329 } | 365 } |
| 330 availableWidth -= kIndentLeavingSpaceForControls; | 366 availableWidth -= kIndentLeavingSpaceForControls; |
| 331 | 367 |
| 332 // Add back in the amount we "get back" from the tabs overlapping. | 368 // Add back in the amount we "get back" from the tabs overlapping. |
| 333 availableWidth += ([tabContentsArray_ count] - 1) * kTabOverlap; | 369 availableWidth += ([tabContentsArray_ count] - 1) * kTabOverlap; |
| 334 const float baseTabWidth = | 370 const float baseTabWidth = |
| 335 MAX(MIN(availableWidth / [tabContentsArray_ count], | 371 MAX(MIN(availableWidth / [tabContentsArray_ count], |
| 336 kMaxTabWidth), | 372 kMaxTabWidth), |
| 337 kMinTabWidth); | 373 kMinTabWidth); |
| 338 | 374 |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 411 offset -= kTabOverlap; | 447 offset -= kTabOverlap; |
| 412 i++; | 448 i++; |
| 413 } | 449 } |
| 414 | 450 |
| 415 // Hide the new tab button if we're explicitly told to. It may already | 451 // Hide the new tab button if we're explicitly told to. It may already |
| 416 // be hidden, doing it again doesn't hurt. | 452 // be hidden, doing it again doesn't hurt. |
| 417 if (forceNewTabButtonHidden_) { | 453 if (forceNewTabButtonHidden_) { |
| 418 [newTabButton_ setHidden:YES]; | 454 [newTabButton_ setHidden:YES]; |
| 419 } else { | 455 } else { |
| 420 NSRect newTabNewFrame = [newTabButton_ frame]; | 456 NSRect newTabNewFrame = [newTabButton_ frame]; |
| 421 if ([self useFullWidthForLayout]) | 457 if ([self inRapidClosureMode]) |
| 458 newTabNewFrame.origin = NSMakePoint(offset + kNewTabButtonOffset, 0); |
| 459 else |
| 422 newTabNewFrame.origin = | 460 newTabNewFrame.origin = |
| 423 NSMakePoint(MIN(availableWidth, offset + kNewTabButtonOffset), 0); | 461 NSMakePoint(MIN(availableWidth, offset + kNewTabButtonOffset), 0); |
| 424 else | |
| 425 newTabNewFrame.origin = NSMakePoint(offset + kNewTabButtonOffset, 0); | |
| 426 newTabNewFrame.origin.x = MAX(newTabNewFrame.origin.x, | 462 newTabNewFrame.origin.x = MAX(newTabNewFrame.origin.x, |
| 427 NSMaxX(placeholderFrame_)); | 463 NSMaxX(placeholderFrame_)); |
| 428 if (i > 0 && [newTabButton_ isHidden]) { | 464 if (i > 0 && [newTabButton_ isHidden]) { |
| 429 id target = animate ? [newTabButton_ animator] : newTabButton_; | 465 id target = animate ? [newTabButton_ animator] : newTabButton_; |
| 430 [target setHidden:NO]; | 466 [target setHidden:NO]; |
| 431 } | 467 } |
| 432 | 468 |
| 433 if (!NSEqualRects(newTabTargetFrame_, newTabNewFrame)) { | 469 if (!NSEqualRects(newTabTargetFrame_, newTabNewFrame)) { |
| 434 [newTabButton_ setFrame:newTabNewFrame]; | 470 [newTabButton_ setFrame:newTabNewFrame]; |
| 435 newTabTargetFrame_ = newTabNewFrame; | 471 newTabTargetFrame_ = newTabNewFrame; |
| (...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 769 | 805 |
| 770 // Called when the tab strip view changes size. As we only registered for | 806 // Called when the tab strip view changes size. As we only registered for |
| 771 // changes on our view, we know it's only for our view. Layout w/out | 807 // changes on our view, we know it's only for our view. Layout w/out |
| 772 // animations since they are blocked by the resize nested runloop. We need | 808 // animations since they are blocked by the resize nested runloop. We need |
| 773 // the views to adjust immediately. Neither the tabs nor their z-order are | 809 // the views to adjust immediately. Neither the tabs nor their z-order are |
| 774 // changed, so we don't need to update the subviews. | 810 // changed, so we don't need to update the subviews. |
| 775 - (void)tabViewFrameChanged:(NSNotification*)info { | 811 - (void)tabViewFrameChanged:(NSNotification*)info { |
| 776 [self layoutTabsWithAnimation:NO regenerateSubviews:NO]; | 812 [self layoutTabsWithAnimation:NO regenerateSubviews:NO]; |
| 777 } | 813 } |
| 778 | 814 |
| 779 - (BOOL)useFullWidthForLayout { | 815 - (BOOL)inRapidClosureMode { |
| 780 return availableResizeWidth_ == kUseFullAvailableWidth; | 816 return availableResizeWidth_ != kUseFullAvailableWidth; |
| 781 } | 817 } |
| 782 | 818 |
| 783 - (void)mouseMoved:(NSEvent *)event { | 819 - (void)mouseMoved:(NSEvent *)event { |
| 784 // Use hit test to figure out what view we are hovering over. | 820 // Use hit test to figure out what view we are hovering over. |
| 785 TabView* targetView = (TabView*)[tabView_ hitTest:[event locationInWindow]]; | 821 TabView* targetView = (TabView*)[tabView_ hitTest:[event locationInWindow]]; |
| 786 if (![targetView isKindOfClass:[TabView class]]) { | 822 if (![targetView isKindOfClass:[TabView class]]) { |
| 787 if ([[targetView superview] isKindOfClass:[TabView class]]) { | 823 if ([[targetView superview] isKindOfClass:[TabView class]]) { |
| 788 targetView = (TabView*)[targetView superview]; | 824 targetView = (TabView*)[targetView superview]; |
| 789 } else { | 825 } else { |
| 790 targetView = nil; | 826 targetView = nil; |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 909 BrowserWindowController* controller = | 945 BrowserWindowController* controller = |
| 910 (BrowserWindowController*)[[switchView_ window] windowController]; | 946 (BrowserWindowController*)[[switchView_ window] windowController]; |
| 911 DCHECK(index >= 0); | 947 DCHECK(index >= 0); |
| 912 if (index >= 0) { | 948 if (index >= 0) { |
| 913 [controller setTab:[self viewAtIndex:index] isDraggable:YES]; | 949 [controller setTab:[self viewAtIndex:index] isDraggable:YES]; |
| 914 } | 950 } |
| 915 } | 951 } |
| 916 | 952 |
| 917 | 953 |
| 918 @end | 954 @end |
| OLD | NEW |