Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(78)

Side by Side Diff: chrome/browser/cocoa/tab_controller.mm

Issue 661265: P2.1a.
Patch Set: '' Created 10 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/cocoa/tab_controller.h ('k') | chrome/browser/cocoa/tab_strip_controller.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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_controller.h"
6
7 #import <Cocoa/Cocoa.h>
8
5 #include "app/l10n_util_mac.h" 9 #include "app/l10n_util_mac.h"
6 #include "base/mac_util.h" 10 #include "base/mac_util.h"
7 #import "chrome/browser/browser_theme_provider.h" 11 #import "chrome/browser/browser_theme_provider.h"
8 #import "chrome/browser/cocoa/menu_controller.h" 12 #import "chrome/browser/cocoa/menu_controller.h"
9 #import "chrome/browser/cocoa/tab_controller.h"
10 #import "chrome/browser/cocoa/tab_controller_target.h" 13 #import "chrome/browser/cocoa/tab_controller_target.h"
11 #import "chrome/browser/cocoa/tab_view.h" 14 #import "chrome/browser/cocoa/tab_view.h"
12 #import "chrome/browser/cocoa/themed_window.h" 15 #import "chrome/browser/cocoa/themed_window.h"
16 #import "chrome/browser/cocoa/throbber_view.h"
13 #include "grit/generated_resources.h" 17 #include "grit/generated_resources.h"
14 18
15 @implementation TabController 19 @implementation TabController
16 20
17 @synthesize loadingState = loadingState_; 21 @synthesize loadingState = loadingState_;
18 @synthesize pinned = pinned_; 22 @synthesize pinned = pinned_;
19 @synthesize target = target_; 23 @synthesize target = target_;
20 @synthesize action = action_; 24 @synthesize action = action_;
21 25
22 namespace TabControllerInternal { 26 namespace TabControllerInternal {
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
68 72
69 // The min widths match the windows values and are sums of left + right 73 // The min widths match the windows values and are sums of left + right
70 // padding, of which we have no comparable constants (we draw using paths, not 74 // padding, of which we have no comparable constants (we draw using paths, not
71 // images). The selected tab width includes the close button width. 75 // images). The selected tab width includes the close button width.
72 + (CGFloat)minTabWidth { return 31; } 76 + (CGFloat)minTabWidth { return 31; }
73 + (CGFloat)minSelectedTabWidth { return 47; } 77 + (CGFloat)minSelectedTabWidth { return 47; }
74 + (CGFloat)maxTabWidth { return 220; } 78 + (CGFloat)maxTabWidth { return 220; }
75 + (CGFloat)pinnedTabWidth { return 53; } 79 + (CGFloat)pinnedTabWidth { return 53; }
76 80
77 - (TabView*)tabView { 81 - (TabView*)tabView {
82 DCHECK([[self view] isKindOfClass:[TabView class]]);
78 return static_cast<TabView*>([self view]); 83 return static_cast<TabView*>([self view]);
79 } 84 }
80 85
81 - (id)init { 86 - (id)init {
82 self = [super initWithNibName:@"TabView" bundle:mac_util::MainAppBundle()]; 87 self = [super initWithNibName:@"TabView" bundle:mac_util::MainAppBundle()];
83 if (self != nil) { 88 if (self != nil) {
84 isIconShowing_ = YES; 89 isIconShowing_ = YES;
85 NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter]; 90 NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
86 [defaultCenter addObserver:self 91 [defaultCenter addObserver:self
87 selector:@selector(viewResized:) 92 selector:@selector(viewResized:)
(...skipping 10 matching lines...) Expand all
98 - (void)dealloc { 103 - (void)dealloc {
99 [[NSNotificationCenter defaultCenter] removeObserver:self]; 104 [[NSNotificationCenter defaultCenter] removeObserver:self];
100 [super dealloc]; 105 [super dealloc];
101 } 106 }
102 107
103 // The internals of |-setSelected:| but doesn't check if we're already set 108 // The internals of |-setSelected:| but doesn't check if we're already set
104 // to |selected|. Pass the selection change to the subviews that need it and 109 // to |selected|. Pass the selection change to the subviews that need it and
105 // mark ourselves as needing a redraw. 110 // mark ourselves as needing a redraw.
106 - (void)internalSetSelected:(BOOL)selected { 111 - (void)internalSetSelected:(BOOL)selected {
107 selected_ = selected; 112 selected_ = selected;
108 TabView* tabView = static_cast<TabView*>([self view]); 113 TabView* tabView = [self tabView];
109 DCHECK([tabView isKindOfClass:[TabView class]]);
110 [tabView setState:selected]; 114 [tabView setState:selected];
111 [tabView cancelAlert]; 115 [tabView cancelAlert];
112 [self updateVisibility]; 116 [self updateVisibility];
113 [self updateTitleColor]; 117 [self updateTitleColor];
114 } 118 }
115 119
116 // Called when the tab's nib is done loading and all outlets are hooked up. 120 // Called when the tab's nib is done loading and all outlets are hooked up.
117 - (void)awakeFromNib { 121 - (void)awakeFromNib {
118 // Remember the icon's frame, so that if the icon is ever removed, a new 122 // Remember the icon's frame, so that if the icon is ever removed, a new
119 // one can later replace it in the proper location. 123 // one can later replace it in the proper location.
120 originalIconFrame_ = [iconView_ frame]; 124 originalIconFrame_ = [iconView_ frame];
121 125
122 // When the icon is removed, the title expands to the left to fill the space 126 // When the icon is removed, the title expands to the left to fill the space
123 // left by the icon. When the close button is removed, the title expands to 127 // left by the icon. This is the amounts to expand and contracttitleView_
124 // the right to fill its space. These are the amounts to expand and contract 128 // under those conditions. FIXME: update comment, simplify logic
125 // titleView_ under those conditions.
126 NSRect titleFrame = [titleView_ frame]; 129 NSRect titleFrame = [titleView_ frame];
127 iconTitleXOffset_ = NSMinX(titleFrame) - NSMinX(originalIconFrame_); 130 // iconTitleXOffset_ = NSMinX(titleFrame) - NSMinX(originalIconFrame_);
128 titleCloseWidthOffset_ = NSMaxX([closeButton_ frame]) - NSMaxX(titleFrame); 131 iconTitleXOffset_ = NSMinX(titleFrame) - NSMaxX(originalIconFrame_);
132 titleTabOffset_ = NSWidth([[self view] frame]) - NSMaxX(titleFrame);
133
129 134
130 [self internalSetSelected:selected_]; 135 [self internalSetSelected:selected_];
131 } 136 }
132 137
133 // Called when Cocoa wants to display the context menu. Lazily instantiate 138 // Called when Cocoa wants to display the context menu. Lazily instantiate
134 // the menu based off of the cross-platform model. Re-create the menu and 139 // the menu based off of the cross-platform model. Re-create the menu and
135 // model every time to get the correct labels and enabling. 140 // model every time to get the correct labels and enabling.
136 - (NSMenu*)menu { 141 - (NSMenu*)menu {
137 contextMenuDelegate_.reset( 142 contextMenuDelegate_.reset(
138 new TabControllerInternal::MenuDelegate(target_, self)); 143 new TabControllerInternal::MenuDelegate(target_, self));
139 contextMenuModel_.reset(new TabMenuModel(contextMenuDelegate_.get())); 144 contextMenuModel_.reset(new TabMenuModel(contextMenuDelegate_.get()));
140 contextMenuController_.reset( 145 contextMenuController_.reset(
141 [[MenuController alloc] initWithModel:contextMenuModel_.get() 146 [[MenuController alloc] initWithModel:contextMenuModel_.get()
142 useWithPopUpButtonCell:NO]); 147 useWithPopUpButtonCell:NO]);
143 return [contextMenuController_ menu]; 148 return [contextMenuController_ menu];
144 } 149 }
145 150
146 - (IBAction)closeTab:(id)sender { 151 - (IBAction)closeTab:(id)sender {
152 if (![self closeButtonActive]) {
153
154 NSEvent* down = [NSApp currentEvent];
155 NSEvent* fakeUp = [NSEvent mouseEventWithType:NSLeftMouseUp
156 location:[down locationInWindow]
157 modifierFlags:[down modifierFlags]
158 timestamp:[down timestamp]
159 windowNumber:[down windowNumber]
160 context:[down context]
161 eventNumber:0 // FIXME
162 clickCount:1
163 pressure:0];
164
165 [closeButton_ mouseUp:fakeUp]; // clear "pressed" state
166 [closeButton_ setState:NSOffState];
167 [[self view] mouseDown:[NSApp currentEvent]];
168 return;
169 }
170
147 if ([[self target] respondsToSelector:@selector(closeTab:)]) { 171 if ([[self target] respondsToSelector:@selector(closeTab:)]) {
148 [[self target] performSelector:@selector(closeTab:) 172 [[self target] performSelector:@selector(closeTab:)
149 withObject:[self view]]; 173 withObject:[self view]];
150 } 174 }
151 } 175 }
152 176
153 - (void)setTitle:(NSString*)title { 177 - (void)setTitle:(NSString*)title {
154 [[self view] setToolTip:title]; 178 [[self view] setToolTip:title];
155 if ([self pinned] && ![self selected]) { 179 if ([self pinned] && ![self selected])
156 TabView* tabView = static_cast<TabView*>([self view]); 180 [[self tabView] startAlert];
157 DCHECK([tabView isKindOfClass:[TabView class]]);
158 [tabView startAlert];
159 }
160 [super setTitle:title]; 181 [super setTitle:title];
161 } 182 }
162 183
163 - (void)setSelected:(BOOL)selected { 184 - (void)setSelected:(BOOL)selected {
164 if (selected_ != selected) 185 if (selected_ != selected)
165 [self internalSetSelected:selected]; 186 [self internalSetSelected:selected];
166 } 187 }
167 188
168 - (BOOL)selected { 189 - (BOOL)selected {
169 return selected_; 190 return selected_;
(...skipping 13 matching lines...) Expand all
183 } 204 }
184 205
185 - (NSView*)iconView { 206 - (NSView*)iconView {
186 return iconView_; 207 return iconView_;
187 } 208 }
188 209
189 - (NSString*)toolTip { 210 - (NSString*)toolTip {
190 return [[self view] toolTip]; 211 return [[self view] toolTip];
191 } 212 }
192 213
193 // Return a rough approximation of the number of icons we could fit in the 214 - (void)updateVisibility {
194 // tab. We never actually do this, but it's a helpful guide for determining 215 // Cocoa apparently gets confused if the title autoresizes too small, so
195 // how much space we have available. 216 // handle this manually. FIXME: comment slightly wrong
196 - (int)iconCapacity { 217 CGFloat titleWidth = NSWidth([[self view] frame]) -
197 CGFloat width = NSMaxX([closeButton_ frame]) - NSMinX(originalIconFrame_); 218 titleTabOffset_ -
198 CGFloat iconWidth = NSWidth(originalIconFrame_); 219 NSMaxX(originalIconFrame_) -
220 iconTitleXOffset_;
199 221
200 return width / iconWidth; 222 bool showClose = NO;
201 } 223 if ([[self tabView] isMouseInside]) {
202 224 if ([self selected]) {
203 // Returns YES if we should show the icon. When tabs get too small, we clip 225 showClose = YES;
204 // the favicon before the close button for selected tabs, and prefer the
205 // favicon for unselected tabs. The icon can also be suppressed more directly
206 // by clearing iconView_.
207 - (BOOL)shouldShowIcon {
208 if (!iconView_)
209 return NO;
210
211 if ([self pinned])
212 return YES;
213
214 int iconCapacity = [self iconCapacity];
215 if ([self selected])
216 return iconCapacity >= 2;
217 return iconCapacity >= 1;
218 }
219
220 // Returns YES if we should be showing the close button. The selected tab
221 // always shows the close button.
222 - (BOOL)shouldShowCloseButton {
223 if ([self pinned])
224 return NO;
225 return ([self selected] || [self iconCapacity] >= 3);
226 }
227
228 - (void)updateVisibility {
229 // iconView_ may have been replaced or it may be nil, so [iconView_ isHidden]
230 // won't work. Instead, the state of the icon is tracked separately in
231 // isIconShowing_.
232 BOOL oldShowIcon = isIconShowing_ ? YES : NO;
233 BOOL newShowIcon = [self shouldShowIcon] ? YES : NO;
234
235 [iconView_ setHidden:newShowIcon ? NO : YES];
236 isIconShowing_ = newShowIcon;
237
238 // If the tab is pinned, hide the title.
239 [titleView_ setHidden:[self pinned]];
240
241 BOOL oldShowCloseButton = [closeButton_ isHidden] ? NO : YES;
242 BOOL newShowCloseButton = [self shouldShowCloseButton] ? YES : NO;
243
244 [closeButton_ setHidden:newShowCloseButton ? NO : YES];
245
246 // Adjust the title view based on changes to the icon's and close button's
247 // visibility.
248 NSRect titleFrame = [titleView_ frame];
249
250 if (oldShowIcon != newShowIcon) {
251 // Adjust the left edge of the title view according to the presence or
252 // absence of the icon view.
253
254 if (newShowIcon) {
255 titleFrame.origin.x += iconTitleXOffset_;
256 titleFrame.size.width -= iconTitleXOffset_;
257 } else { 226 } else {
258 titleFrame.origin.x -= iconTitleXOffset_; 227 // To make accidental tab closure less likely, only show the close button
259 titleFrame.size.width += iconTitleXOffset_; 228 // on hover in background tabs if they are "wide enough".
229 showClose = titleWidth >= 2*NSWidth(originalIconFrame_) || !iconView_;
260 } 230 }
231 } else if ([self selected]) {
232 // FIXME: this is a bit of a hack; use an explicit method
233 BOOL isLoading = [iconView_ isKindOfClass:[ThrobberView class]];
234 showClose = isLoading ? NO : YES;
235 } else {
236 // NTP should always show close, even if in background (it doesn't have
237 // an icon).
238 showClose = iconView_ ? NO : YES;
261 } 239 }
262 240
263 if (oldShowCloseButton != newShowCloseButton) { 241 if (titleWidth > 0) {
264 // Adjust the right edge of the title view according to the presence or 242 NSRect titleFrame = [titleView_ frame];
265 // absence of the close button. 243 titleFrame.size.width = titleWidth;
266 if (newShowCloseButton) 244 [titleView_ setHidden:NO];
267 titleFrame.size.width -= titleCloseWidthOffset_; 245 [titleView_ setFrame:titleFrame];
268 else 246 } else {
269 titleFrame.size.width += titleCloseWidthOffset_; 247 [titleView_ setHidden:YES];
270 } 248 }
271 249
272 [titleView_ setFrame:titleFrame]; 250 // Don't show icon if tab is not active and tab is very small. The tab strip
251 // controller takes care to always keep the active tab wide enough for a close
252 // button.
253 if (NSWidth([[self view] frame]) >= [TabController minSelectedTabWidth]) {
254 if (showClose && [closeButton_ isHidden]) {
255 closeButtonRevealTime_ = [NSDate timeIntervalSinceReferenceDate];
256 }
257
258 [closeButton_ setHidden:showClose ? NO : YES];
259 [iconView_ setHidden:showClose ? YES : NO];
260 } else {
261 [closeButton_ setHidden:YES];
262 [iconView_ setHidden:YES];
263 }
273 } 264 }
274 265
275 - (void)updateTitleColor { 266 - (void)updateTitleColor {
276 NSColor* titleColor = nil; 267 NSColor* titleColor = nil;
277 ThemeProvider* theme = [[[self view] window] themeProvider]; 268 ThemeProvider* theme = [[[self view] window] themeProvider];
278 if (theme && ![self selected]) { 269 if (theme && ![self selected]) {
279 titleColor = 270 titleColor =
280 theme->GetNSColor(BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT, 271 theme->GetNSColor(BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT,
281 true); 272 true);
282 } 273 }
283 // Default to the selected text color unless told otherwise. 274 // Default to the selected text color unless told otherwise.
284 if (theme && !titleColor) { 275 if (theme && !titleColor) {
285 titleColor = theme->GetNSColor(BrowserThemeProvider::COLOR_TAB_TEXT, 276 titleColor = theme->GetNSColor(BrowserThemeProvider::COLOR_TAB_TEXT,
286 true); 277 true);
287 } 278 }
288 [titleView_ setTextColor:titleColor ? titleColor : [NSColor textColor]]; 279 [titleView_ setTextColor:titleColor ? titleColor : [NSColor textColor]];
289 } 280 }
290 281
282 - (BOOL)closeButtonActive {
283 if ([closeButton_ isHidden]) return false;
284 if ([self selected] || [self inRapidClosureMode]) return true;
285 NSTimeInterval closeButtonScreenTime =
286 [NSDate timeIntervalSinceReferenceDate] - closeButtonRevealTime_;
287 fprintf(stderr, " %f\n", closeButtonScreenTime);
288 return ![closeButton_ isHidden] && closeButtonScreenTime > 1.0;
289
290 }
291
291 // Called when our view is resized. If it gets too small, start by hiding 292 // Called when our view is resized. If it gets too small, start by hiding
292 // the close button and only show it if tab is selected. Eventually, hide the 293 // the close button and only show it if tab is selected. Eventually, hide the
293 // icon as well. We know that this is for our view because we only registered 294 // icon as well. We know that this is for our view because we only registered
294 // for notifications from our specific view. 295 // for notifications from our specific view. FIXME: update comment
295 - (void)viewResized:(NSNotification*)info { 296 - (void)viewResized:(NSNotification*)info {
296 [self updateVisibility]; 297 [self updateVisibility];
297 } 298 }
298 299
299 - (void)themeChangedNotification:(NSNotification*)notification { 300 - (void)themeChangedNotification:(NSNotification*)notification {
300 [self updateTitleColor]; 301 [self updateTitleColor];
301 } 302 }
302 303
303 // Called by the tabs to determine whether we are in rapid (tab) closure mode. 304 // Called by the tabs to determine whether we are in rapid (tab) closure mode.
304 - (BOOL)inRapidClosureMode { 305 - (BOOL)inRapidClosureMode {
305 if ([[self target] respondsToSelector:@selector(inRapidClosureMode)]) { 306 if ([[self target] respondsToSelector:@selector(inRapidClosureMode)]) {
306 return [[self target] performSelector:@selector(inRapidClosureMode)] ? 307 return [[self target] performSelector:@selector(inRapidClosureMode)] ?
307 YES : NO; 308 YES : NO;
308 } 309 }
309 return NO; 310 return NO;
310 } 311 }
311 312
312 @end 313 @end
OLDNEW
« no previous file with comments | « chrome/browser/cocoa/tab_controller.h ('k') | chrome/browser/cocoa/tab_strip_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698