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 |