| 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 #include "base/mac_util.h" | 5 #include "base/mac_util.h" |
| 6 #include "chrome/browser/cocoa/nsimage_cache.h" | 6 #include "chrome/browser/cocoa/nsimage_cache.h" |
| 7 #import "chrome/browser/cocoa/tab_controller.h" | 7 #import "chrome/browser/cocoa/tab_controller.h" |
| 8 #import "chrome/browser/cocoa/tab_controller_target.h" | 8 #import "chrome/browser/cocoa/tab_controller_target.h" |
| 9 #import "chrome/browser/cocoa/tab_view.h" | 9 #import "chrome/browser/cocoa/tab_view.h" |
| 10 #import "third_party/GTM/AppKit/GTMTheme.h" | 10 #import "third_party/GTM/AppKit/GTMTheme.h" |
| 11 | 11 |
| 12 @interface TabController(Private) | 12 @interface TabController(Private) |
| 13 - (void)updateVisibility; | 13 - (void)updateVisibility; |
| 14 @end | 14 @end |
| 15 | 15 |
| 16 @implementation TabController | 16 @implementation TabController |
| 17 | 17 |
| 18 @synthesize loadingState = loadingState_; | 18 @synthesize loadingState = loadingState_; |
| 19 @synthesize target = target_; | 19 @synthesize target = target_; |
| 20 @synthesize action = action_; | 20 @synthesize action = action_; |
| 21 | 21 |
| 22 // The min widths match the windows values and are sums of left + right | 22 // The min widths match the windows values and are sums of left + right |
| 23 // padding, of which we have no comparable constants (we draw using paths, not | 23 // padding, of which we have no comparable constants (we draw using paths, not |
| 24 // images). The selected tab width includes the close box width. | 24 // images). The selected tab width includes the close button width. |
| 25 + (float)minTabWidth { return 31; } | 25 + (float)minTabWidth { return 31; } |
| 26 + (float)minSelectedTabWidth { return 47; } | 26 + (float)minSelectedTabWidth { return 47; } |
| 27 + (float)maxTabWidth { return 220.0; } | 27 + (float)maxTabWidth { return 220; } |
| 28 | 28 |
| 29 - (TabView*)tabView { | 29 - (TabView*)tabView { |
| 30 return static_cast<TabView*>([self view]); | 30 return static_cast<TabView*>([self view]); |
| 31 } | 31 } |
| 32 | 32 |
| 33 - (id)init { | 33 - (id)init { |
| 34 self = [super initWithNibName:@"TabView" bundle:mac_util::MainAppBundle()]; | 34 self = [super initWithNibName:@"TabView" bundle:mac_util::MainAppBundle()]; |
| 35 if (self != nil) { | 35 if (self != nil) { |
| 36 isIconShowing_ = YES; |
| 36 [[NSNotificationCenter defaultCenter] | 37 [[NSNotificationCenter defaultCenter] |
| 37 addObserver:self | 38 addObserver:self |
| 38 selector:@selector(viewResized:) | 39 selector:@selector(viewResized:) |
| 39 name:NSViewFrameDidChangeNotification | 40 name:NSViewFrameDidChangeNotification |
| 40 object:[self view]]; | 41 object:[self view]]; |
| 41 } | 42 } |
| 42 return self; | 43 return self; |
| 43 } | 44 } |
| 44 | 45 |
| 45 - (void)dealloc { | 46 - (void)dealloc { |
| 46 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 47 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 47 [super dealloc]; | 48 [super dealloc]; |
| 48 } | 49 } |
| 49 | 50 |
| 50 // The internals of |-setSelected:| but doesn't check if we're already set | 51 // The internals of |-setSelected:| but doesn't check if we're already set |
| 51 // to |selected|. Pass the selection change to the subviews that need it and | 52 // to |selected|. Pass the selection change to the subviews that need it and |
| 52 // mark ourselves as needing a redraw. | 53 // mark ourselves as needing a redraw. |
| 53 - (void)internalSetSelected:(BOOL)selected { | 54 - (void)internalSetSelected:(BOOL)selected { |
| 54 selected_ = selected; | 55 selected_ = selected; |
| 55 [(TabView *)[self view] setState:selected]; | 56 [(TabView *)[self view] setState:selected]; |
| 56 [self updateVisibility]; | 57 [self updateVisibility]; |
| 57 [self applyTheme]; | 58 [self applyTheme]; |
| 58 } | 59 } |
| 59 | 60 |
| 60 // Called when the tab's nib is done loading and all outlets are hooked up. | 61 // Called when the tab's nib is done loading and all outlets are hooked up. |
| 61 - (void)awakeFromNib { | 62 - (void)awakeFromNib { |
| 63 // Remember the icon's frame, so that if the icon is ever removed, a new |
| 64 // one can later replace it in the proper location. |
| 65 originalIconFrame_ = [iconView_ frame]; |
| 66 |
| 67 // When the icon is removed, the title expands to the left to fill the space |
| 68 // left by the icon. When the close button is removed, the title expands to |
| 69 // the right to fill its space. These are the amounts to expand and contract |
| 70 // titleView_ under those conditions. |
| 71 NSRect titleFrame = [titleView_ frame]; |
| 72 iconTitleXOffset_ = NSMinX(titleFrame) - NSMinX(originalIconFrame_); |
| 73 titleCloseWidthOffset_ = NSMaxX([closeButton_ frame]) - NSMaxX(titleFrame); |
| 74 |
| 62 // Ensure we don't show favicon if the tab is already too small to begin with. | 75 // Ensure we don't show favicon if the tab is already too small to begin with. |
| 63 [self updateVisibility]; | 76 [self updateVisibility]; |
| 64 [(id)iconView_ setImage:nsimage_cache::ImageNamed(@"nav.pdf")]; | 77 |
| 65 [self internalSetSelected:selected_]; | 78 [self internalSetSelected:selected_]; |
| 66 } | 79 } |
| 67 | 80 |
| 68 - (IBAction)closeTab:(id)sender { | 81 - (IBAction)closeTab:(id)sender { |
| 69 if ([[self target] respondsToSelector:@selector(closeTab:)]) { | 82 if ([[self target] respondsToSelector:@selector(closeTab:)]) { |
| 70 [[self target] performSelector:@selector(closeTab:) | 83 [[self target] performSelector:@selector(closeTab:) |
| 71 withObject:[self view]]; | 84 withObject:[self view]]; |
| 72 } | 85 } |
| 73 } | 86 } |
| 74 | 87 |
| (...skipping 21 matching lines...) Expand all Loading... |
| 96 - (void)setSelected:(BOOL)selected { | 109 - (void)setSelected:(BOOL)selected { |
| 97 if (selected_ != selected) | 110 if (selected_ != selected) |
| 98 [self internalSetSelected:selected]; | 111 [self internalSetSelected:selected]; |
| 99 } | 112 } |
| 100 | 113 |
| 101 - (BOOL)selected { | 114 - (BOOL)selected { |
| 102 return selected_; | 115 return selected_; |
| 103 } | 116 } |
| 104 | 117 |
| 105 - (void)setIconView:(NSView*)iconView { | 118 - (void)setIconView:(NSView*)iconView { |
| 106 NSRect currentFrame = [iconView_ frame]; | |
| 107 [iconView_ removeFromSuperview]; | 119 [iconView_ removeFromSuperview]; |
| 108 iconView_ = iconView; | 120 iconView_ = iconView; |
| 109 [iconView_ setFrame:currentFrame]; | 121 [iconView_ setFrame:originalIconFrame_]; |
| 110 // Ensure we don't show favicon if the tab is already too small to begin with. | 122 |
| 123 // Ensure that the icon is suppressed if no icon is set or if the tab is too |
| 124 // narrow to display one. |
| 111 [self updateVisibility]; | 125 [self updateVisibility]; |
| 112 [[self view] addSubview:iconView_]; | 126 |
| 127 if (iconView_) |
| 128 [[self view] addSubview:iconView_]; |
| 113 } | 129 } |
| 114 | 130 |
| 115 - (NSView*)iconView { | 131 - (NSView*)iconView { |
| 116 return iconView_; | 132 return iconView_; |
| 117 } | 133 } |
| 118 | 134 |
| 119 - (NSString *)toolTip { | 135 - (NSString *)toolTip { |
| 120 return [[self view] toolTip]; | 136 return [[self view] toolTip]; |
| 121 } | 137 } |
| 122 | 138 |
| 123 // Return a rough approximation of the number of icons we could fit in the | 139 // Return a rough approximation of the number of icons we could fit in the |
| 124 // tab. We never actually do this, but it's a helpful guide for determining | 140 // tab. We never actually do this, but it's a helpful guide for determining |
| 125 // how much space we have available. | 141 // how much space we have available. |
| 126 - (int)iconCapacity { | 142 - (int)iconCapacity { |
| 127 float width = NSWidth([[self view] frame]); | 143 float width = NSMaxX([closeButton_ frame]) - NSMinX(originalIconFrame_); |
| 128 float leftPadding = NSMinX([iconView_ frame]); | 144 float iconWidth = NSWidth(originalIconFrame_); |
| 129 float rightPadding = width - NSMaxX([closeButton_ frame]); | |
| 130 float iconWidth = NSWidth([iconView_ frame]); | |
| 131 | 145 |
| 132 width -= leftPadding + rightPadding; | |
| 133 return width / iconWidth; | 146 return width / iconWidth; |
| 134 } | 147 } |
| 135 | 148 |
| 136 // Returns YES if we should show the icon. When tabs get too small, we clip | 149 // Returns YES if we should show the icon. When tabs get too small, we clip |
| 137 // the favicon before the close box for selected tabs, and prefer the favicon | 150 // the favicon before the close button for selected tabs, and prefer the |
| 138 // for unselected tabs. | 151 // favicon for unselected tabs. The icon can also be suppressed more directly |
| 139 // TODO(pinkerton): don't show the icon if there's no image data (eg, NTP). | 152 // by clearing iconView_. |
| 140 - (BOOL)shouldShowIcon { | 153 - (BOOL)shouldShowIcon { |
| 154 if (!iconView_) |
| 155 return NO; |
| 156 |
| 141 int iconCapacity = [self iconCapacity]; | 157 int iconCapacity = [self iconCapacity]; |
| 142 if ([self selected]) | 158 if ([self selected]) |
| 143 return iconCapacity >= 2; | 159 return iconCapacity >= 2; |
| 144 return iconCapacity >= 1; | 160 return iconCapacity >= 1; |
| 145 } | 161 } |
| 146 | 162 |
| 147 // Returns YES if we should be showing the close box. The selected tab always | 163 // Returns YES if we should be showing the close button. The selected tab |
| 148 // shows the close box. | 164 // always shows the close button. |
| 149 - (BOOL)shouldShowCloseBox { | 165 - (BOOL)shouldShowCloseButton { |
| 150 return [self selected] || [self iconCapacity] >= 3; | 166 return [self selected] || [self iconCapacity] >= 3; |
| 151 } | 167 } |
| 152 | 168 |
| 153 // Call to update the visibility of certain subviews, such as the icon or | 169 // Updates the visibility of certain subviews, such as the icon and close |
| 154 // close box, based on criteria like if the tab is selected and the current | 170 // button, based on criteria such as the tab's selected state and its current |
| 155 // tab width. | 171 // width. |
| 156 - (void)updateVisibility { | 172 - (void)updateVisibility { |
| 157 [iconView_ setHidden:[self shouldShowIcon] ? NO : YES]; | 173 // iconView_ may have been replaced or it may be nil, so [iconView_ isHidden] |
| 158 [closeButton_ setHidden:[self shouldShowCloseBox] ? NO : YES]; | 174 // won't work. Instead, the state of the icon is tracked separately in |
| 175 // isIconShowing_. |
| 176 BOOL oldShowIcon = isIconShowing_ ? YES : NO; |
| 177 BOOL newShowIcon = [self shouldShowIcon] ? YES : NO; |
| 178 |
| 179 [iconView_ setHidden:newShowIcon ? NO : YES]; |
| 180 isIconShowing_ = newShowIcon; |
| 181 |
| 182 BOOL oldShowCloseButton = [closeButton_ isHidden] ? NO : YES; |
| 183 BOOL newShowCloseButton = [self shouldShowCloseButton] ? YES : NO; |
| 184 |
| 185 [closeButton_ setHidden:newShowCloseButton ? NO : YES]; |
| 186 |
| 187 // Adjust the title view based on changes to the icon's and close button's |
| 188 // visibility. |
| 189 NSRect titleFrame = [titleView_ frame]; |
| 190 |
| 191 if (oldShowIcon != newShowIcon) { |
| 192 // Adjust the left edge of the title view according to the presence or |
| 193 // absence of the icon view. |
| 194 |
| 195 if (newShowIcon) { |
| 196 titleFrame.origin.x += iconTitleXOffset_; |
| 197 titleFrame.size.width -= iconTitleXOffset_; |
| 198 } else { |
| 199 titleFrame.origin.x -= iconTitleXOffset_; |
| 200 titleFrame.size.width += iconTitleXOffset_; |
| 201 } |
| 202 } |
| 203 |
| 204 if (oldShowCloseButton != newShowCloseButton) { |
| 205 // Adjust the right edge of the title view according to the presence or |
| 206 // absence of the close button. |
| 207 if (newShowCloseButton) |
| 208 titleFrame.size.width -= titleCloseWidthOffset_; |
| 209 else |
| 210 titleFrame.size.width += titleCloseWidthOffset_; |
| 211 } |
| 212 |
| 213 [titleView_ setFrame:titleFrame]; |
| 159 } | 214 } |
| 160 | 215 |
| 161 // Called when our view is resized. If it gets too small, start by hiding | 216 // Called when our view is resized. If it gets too small, start by hiding |
| 162 // the close box and only show it if tab is selected. Eventually, hide the | 217 // the close button and only show it if tab is selected. Eventually, hide the |
| 163 // icon as well. We know that this is for our view because we only registered | 218 // icon as well. We know that this is for our view because we only registered |
| 164 // for notifications from our specific view. | 219 // for notifications from our specific view. |
| 165 - (void)viewResized:(NSNotification*)info { | 220 - (void)viewResized:(NSNotification*)info { |
| 166 [self updateVisibility]; | 221 [self updateVisibility]; |
| 167 } | 222 } |
| 168 | 223 |
| 169 - (void)applyTheme { | 224 - (void)applyTheme { |
| 170 GTMTheme* theme = [[self view] gtm_theme]; | 225 GTMTheme* theme = [[self view] gtm_theme]; |
| 171 NSColor* color = nil; | 226 NSColor* color = nil; |
| 172 if (!selected_) { | 227 if (!selected_) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 186 // Called by the tabs to determine whether we are in rapid (tab) closure mode. | 241 // Called by the tabs to determine whether we are in rapid (tab) closure mode. |
| 187 - (BOOL)inRapidClosureMode { | 242 - (BOOL)inRapidClosureMode { |
| 188 if ([[self target] respondsToSelector:@selector(inRapidClosureMode)]) { | 243 if ([[self target] respondsToSelector:@selector(inRapidClosureMode)]) { |
| 189 return [[self target] performSelector:@selector(inRapidClosureMode)] ? | 244 return [[self target] performSelector:@selector(inRapidClosureMode)] ? |
| 190 YES : NO; | 245 YES : NO; |
| 191 } | 246 } |
| 192 return NO; | 247 return NO; |
| 193 } | 248 } |
| 194 | 249 |
| 195 @end | 250 @end |
| OLD | NEW |