| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #import "chrome/browser/ui/cocoa/tab_window_controller.h" | |
| 6 | |
| 7 #include "app/theme_provider.h" | |
| 8 #include "base/logging.h" | |
| 9 #import "chrome/browser/ui/cocoa/focus_tracker.h" | |
| 10 #import "chrome/browser/ui/cocoa/tab_strip_view.h" | |
| 11 #import "chrome/browser/ui/cocoa/themed_window.h" | |
| 12 | |
| 13 @interface TabWindowController(PRIVATE) | |
| 14 - (void)setUseOverlay:(BOOL)useOverlay; | |
| 15 @end | |
| 16 | |
| 17 @interface TabWindowOverlayWindow : NSWindow | |
| 18 @end | |
| 19 | |
| 20 @implementation TabWindowOverlayWindow | |
| 21 | |
| 22 - (ThemeProvider*)themeProvider { | |
| 23 if ([self parentWindow]) | |
| 24 return [[[self parentWindow] windowController] themeProvider]; | |
| 25 return NULL; | |
| 26 } | |
| 27 | |
| 28 - (ThemedWindowStyle)themedWindowStyle { | |
| 29 if ([self parentWindow]) | |
| 30 return [[[self parentWindow] windowController] themedWindowStyle]; | |
| 31 return NO; | |
| 32 } | |
| 33 | |
| 34 - (NSPoint)themePatternPhase { | |
| 35 if ([self parentWindow]) | |
| 36 return [[[self parentWindow] windowController] themePatternPhase]; | |
| 37 return NSZeroPoint; | |
| 38 } | |
| 39 | |
| 40 @end | |
| 41 | |
| 42 @implementation TabWindowController | |
| 43 @synthesize tabContentArea = tabContentArea_; | |
| 44 | |
| 45 - (id)initWithWindow:(NSWindow*)window { | |
| 46 if ((self = [super initWithWindow:window]) != nil) { | |
| 47 lockedTabs_.reset([[NSMutableSet alloc] initWithCapacity:10]); | |
| 48 } | |
| 49 return self; | |
| 50 } | |
| 51 | |
| 52 // Add the side tab strip to the left side of the window's content area, | |
| 53 // making it fill the full height of the content area. | |
| 54 - (void)addSideTabStripToWindow { | |
| 55 NSView* contentView = [[self window] contentView]; | |
| 56 NSRect contentFrame = [contentView frame]; | |
| 57 NSRect sideStripFrame = | |
| 58 NSMakeRect(0, 0, | |
| 59 NSWidth([sideTabStripView_ frame]), | |
| 60 NSHeight(contentFrame)); | |
| 61 [sideTabStripView_ setFrame:sideStripFrame]; | |
| 62 [contentView addSubview:sideTabStripView_]; | |
| 63 } | |
| 64 | |
| 65 // Add the top tab strop to the window, above the content box and add it to the | |
| 66 // view hierarchy as a sibling of the content view so it can overlap with the | |
| 67 // window frame. | |
| 68 - (void)addTopTabStripToWindow { | |
| 69 NSRect contentFrame = [tabContentArea_ frame]; | |
| 70 NSRect tabFrame = | |
| 71 NSMakeRect(0, NSMaxY(contentFrame), | |
| 72 NSWidth(contentFrame), | |
| 73 NSHeight([topTabStripView_ frame])); | |
| 74 [topTabStripView_ setFrame:tabFrame]; | |
| 75 NSView* contentParent = [[[self window] contentView] superview]; | |
| 76 [contentParent addSubview:topTabStripView_]; | |
| 77 } | |
| 78 | |
| 79 - (void)windowDidLoad { | |
| 80 // Cache the difference in height between the window content area and the | |
| 81 // tab content area. | |
| 82 NSRect tabFrame = [tabContentArea_ frame]; | |
| 83 NSRect contentFrame = [[[self window] contentView] frame]; | |
| 84 contentAreaHeightDelta_ = NSHeight(contentFrame) - NSHeight(tabFrame); | |
| 85 | |
| 86 if ([self hasTabStrip]) { | |
| 87 if ([self useVerticalTabs]) { | |
| 88 // No top tabstrip so remove the tabContentArea offset. | |
| 89 tabFrame.size.height = contentFrame.size.height; | |
| 90 [tabContentArea_ setFrame:tabFrame]; | |
| 91 [self addSideTabStripToWindow]; | |
| 92 } else { | |
| 93 [self addTopTabStripToWindow]; | |
| 94 } | |
| 95 } else { | |
| 96 // No top tabstrip so remove the tabContentArea offset. | |
| 97 tabFrame.size.height = contentFrame.size.height; | |
| 98 [tabContentArea_ setFrame:tabFrame]; | |
| 99 } | |
| 100 } | |
| 101 | |
| 102 // Toggles from one display mode of the tab strip to another. Will automatically | |
| 103 // call -layoutSubviews to reposition other content. | |
| 104 - (void)toggleTabStripDisplayMode { | |
| 105 // Adjust the size of the tab contents to either use more or less space, | |
| 106 // depending on the direction of the toggle. This needs to be done prior to | |
| 107 // adding back in the top tab strip as its position is based off the MaxY | |
| 108 // of the tab content area. | |
| 109 BOOL useVertical = [self useVerticalTabs]; | |
| 110 NSRect tabContentsFrame = [tabContentArea_ frame]; | |
| 111 tabContentsFrame.size.height += useVertical ? | |
| 112 contentAreaHeightDelta_ : -contentAreaHeightDelta_; | |
| 113 [tabContentArea_ setFrame:tabContentsFrame]; | |
| 114 | |
| 115 if (useVertical) { | |
| 116 // Remove the top tab strip and add the sidebar in. | |
| 117 [topTabStripView_ removeFromSuperview]; | |
| 118 [self addSideTabStripToWindow]; | |
| 119 } else { | |
| 120 // Remove the side tab strip and add the top tab strip as a sibling of the | |
| 121 // window's content area. | |
| 122 [sideTabStripView_ removeFromSuperview]; | |
| 123 NSRect tabContentsFrame = [tabContentArea_ frame]; | |
| 124 tabContentsFrame.size.height -= contentAreaHeightDelta_; | |
| 125 [tabContentArea_ setFrame:tabContentsFrame]; | |
| 126 [self addTopTabStripToWindow]; | |
| 127 } | |
| 128 | |
| 129 [self layoutSubviews]; | |
| 130 } | |
| 131 | |
| 132 // Return the appropriate tab strip based on whether or not side tabs are | |
| 133 // enabled. | |
| 134 - (TabStripView*)tabStripView { | |
| 135 if ([self useVerticalTabs]) | |
| 136 return sideTabStripView_; | |
| 137 return topTabStripView_; | |
| 138 } | |
| 139 | |
| 140 - (void)removeOverlay { | |
| 141 [self setUseOverlay:NO]; | |
| 142 if (closeDeferred_) { | |
| 143 // See comment in BrowserWindowCocoa::Close() about orderOut:. | |
| 144 [[self window] orderOut:self]; | |
| 145 [[self window] performClose:self]; // Autoreleases the controller. | |
| 146 } | |
| 147 } | |
| 148 | |
| 149 - (void)showOverlay { | |
| 150 [self setUseOverlay:YES]; | |
| 151 } | |
| 152 | |
| 153 // if |useOverlay| is true, we're moving views into the overlay's content | |
| 154 // area. If false, we're moving out of the overlay back into the window's | |
| 155 // content. | |
| 156 - (void)moveViewsBetweenWindowAndOverlay:(BOOL)useOverlay { | |
| 157 if (useOverlay) { | |
| 158 [[[overlayWindow_ contentView] superview] addSubview:[self tabStripView]]; | |
| 159 // Add the original window's content view as a subview of the overlay | |
| 160 // window's content view. We cannot simply use setContentView: here because | |
| 161 // the overlay window has a different content size (due to it being | |
| 162 // borderless). | |
| 163 [[overlayWindow_ contentView] addSubview:cachedContentView_]; | |
| 164 } else { | |
| 165 [[self window] setContentView:cachedContentView_]; | |
| 166 // The TabStripView always needs to be in front of the window's content | |
| 167 // view and therefore it should always be added after the content view is | |
| 168 // set. | |
| 169 [[[[self window] contentView] superview] addSubview:[self tabStripView]]; | |
| 170 [[[[self window] contentView] superview] updateTrackingAreas]; | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 // If |useOverlay| is YES, creates a new overlay window and puts the tab strip | |
| 175 // and the content area inside of it. This allows it to have a different opacity | |
| 176 // from the title bar. If NO, returns everything to the previous state and | |
| 177 // destroys the overlay window until it's needed again. The tab strip and window | |
| 178 // contents are returned to the original window. | |
| 179 - (void)setUseOverlay:(BOOL)useOverlay { | |
| 180 [NSObject cancelPreviousPerformRequestsWithTarget:self | |
| 181 selector:@selector(removeOverlay) | |
| 182 object:nil]; | |
| 183 NSWindow* window = [self window]; | |
| 184 if (useOverlay && !overlayWindow_) { | |
| 185 DCHECK(!cachedContentView_); | |
| 186 overlayWindow_ = [[TabWindowOverlayWindow alloc] | |
| 187 initWithContentRect:[window frame] | |
| 188 styleMask:NSBorderlessWindowMask | |
| 189 backing:NSBackingStoreBuffered | |
| 190 defer:YES]; | |
| 191 [overlayWindow_ setTitle:@"overlay"]; | |
| 192 [overlayWindow_ setBackgroundColor:[NSColor clearColor]]; | |
| 193 [overlayWindow_ setOpaque:NO]; | |
| 194 [overlayWindow_ setDelegate:self]; | |
| 195 cachedContentView_ = [window contentView]; | |
| 196 [window addChildWindow:overlayWindow_ ordered:NSWindowAbove]; | |
| 197 // Sets explictly nil to the responder and then restores it. | |
| 198 // Leaving the first responder non-null here | |
| 199 // causes [RenderWidgethostViewCocoa resignFirstResponder] and | |
| 200 // following RenderWidgetHost::Blur(), which results unexpected | |
| 201 // focus lost. | |
| 202 focusBeforeOverlay_.reset([[FocusTracker alloc] initWithWindow:window]); | |
| 203 [window makeFirstResponder:nil]; | |
| 204 [self moveViewsBetweenWindowAndOverlay:useOverlay]; | |
| 205 [overlayWindow_ orderFront:nil]; | |
| 206 } else if (!useOverlay && overlayWindow_) { | |
| 207 DCHECK(cachedContentView_); | |
| 208 [window setContentView:cachedContentView_]; | |
| 209 [self moveViewsBetweenWindowAndOverlay:useOverlay]; | |
| 210 [focusBeforeOverlay_ restoreFocusInWindow:window]; | |
| 211 focusBeforeOverlay_.reset(nil); | |
| 212 [window display]; | |
| 213 [window removeChildWindow:overlayWindow_]; | |
| 214 [overlayWindow_ orderOut:nil]; | |
| 215 [overlayWindow_ release]; | |
| 216 overlayWindow_ = nil; | |
| 217 cachedContentView_ = nil; | |
| 218 } else { | |
| 219 NOTREACHED(); | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 - (NSWindow*)overlayWindow { | |
| 224 return overlayWindow_; | |
| 225 } | |
| 226 | |
| 227 - (BOOL)shouldConstrainFrameRect { | |
| 228 // If we currently have an overlay window, do not attempt to change the | |
| 229 // window's size, as our overlay window doesn't know how to resize properly. | |
| 230 return overlayWindow_ == nil; | |
| 231 } | |
| 232 | |
| 233 - (BOOL)canReceiveFrom:(TabWindowController*)source { | |
| 234 // subclass must implement | |
| 235 NOTIMPLEMENTED(); | |
| 236 return NO; | |
| 237 } | |
| 238 | |
| 239 - (void)moveTabView:(NSView*)view | |
| 240 fromController:(TabWindowController*)dragController { | |
| 241 NOTIMPLEMENTED(); | |
| 242 } | |
| 243 | |
| 244 - (NSView*)selectedTabView { | |
| 245 NOTIMPLEMENTED(); | |
| 246 return nil; | |
| 247 } | |
| 248 | |
| 249 - (void)layoutTabs { | |
| 250 // subclass must implement | |
| 251 NOTIMPLEMENTED(); | |
| 252 } | |
| 253 | |
| 254 - (TabWindowController*)detachTabToNewWindow:(TabView*)tabView { | |
| 255 // subclass must implement | |
| 256 NOTIMPLEMENTED(); | |
| 257 return NULL; | |
| 258 } | |
| 259 | |
| 260 - (void)insertPlaceholderForTab:(TabView*)tab | |
| 261 frame:(NSRect)frame | |
| 262 yStretchiness:(CGFloat)yStretchiness { | |
| 263 [self showNewTabButton:NO]; | |
| 264 } | |
| 265 | |
| 266 - (void)removePlaceholder { | |
| 267 [self showNewTabButton:YES]; | |
| 268 } | |
| 269 | |
| 270 - (BOOL)isDragSessionActive { | |
| 271 NOTIMPLEMENTED(); | |
| 272 return NO; | |
| 273 } | |
| 274 | |
| 275 - (BOOL)tabDraggingAllowed { | |
| 276 return YES; | |
| 277 } | |
| 278 | |
| 279 - (BOOL)tabTearingAllowed { | |
| 280 return YES; | |
| 281 } | |
| 282 | |
| 283 - (BOOL)windowMovementAllowed { | |
| 284 return YES; | |
| 285 } | |
| 286 | |
| 287 - (BOOL)isTabFullyVisible:(TabView*)tab { | |
| 288 // Subclasses should implement this, but it's not necessary. | |
| 289 return YES; | |
| 290 } | |
| 291 | |
| 292 - (void)showNewTabButton:(BOOL)show { | |
| 293 // subclass must implement | |
| 294 NOTIMPLEMENTED(); | |
| 295 } | |
| 296 | |
| 297 - (void)detachTabView:(NSView*)view { | |
| 298 // subclass must implement | |
| 299 NOTIMPLEMENTED(); | |
| 300 } | |
| 301 | |
| 302 - (NSInteger)numberOfTabs { | |
| 303 // subclass must implement | |
| 304 NOTIMPLEMENTED(); | |
| 305 return 0; | |
| 306 } | |
| 307 | |
| 308 - (BOOL)hasLiveTabs { | |
| 309 // subclass must implement | |
| 310 NOTIMPLEMENTED(); | |
| 311 return NO; | |
| 312 } | |
| 313 | |
| 314 - (NSString*)selectedTabTitle { | |
| 315 // subclass must implement | |
| 316 NOTIMPLEMENTED(); | |
| 317 return @""; | |
| 318 } | |
| 319 | |
| 320 - (BOOL)hasTabStrip { | |
| 321 // Subclasses should implement this. | |
| 322 NOTIMPLEMENTED(); | |
| 323 return YES; | |
| 324 } | |
| 325 | |
| 326 - (BOOL)useVerticalTabs { | |
| 327 // Subclasses should implement this. | |
| 328 NOTIMPLEMENTED(); | |
| 329 return NO; | |
| 330 } | |
| 331 | |
| 332 - (BOOL)isTabDraggable:(NSView*)tabView { | |
| 333 return ![lockedTabs_ containsObject:tabView]; | |
| 334 } | |
| 335 | |
| 336 - (void)setTab:(NSView*)tabView isDraggable:(BOOL)draggable { | |
| 337 if (draggable) | |
| 338 [lockedTabs_ removeObject:tabView]; | |
| 339 else | |
| 340 [lockedTabs_ addObject:tabView]; | |
| 341 } | |
| 342 | |
| 343 // Tell the window that it needs to call performClose: as soon as the current | |
| 344 // drag is complete. This prevents a window (and its overlay) from going away | |
| 345 // during a drag. | |
| 346 - (void)deferPerformClose { | |
| 347 closeDeferred_ = YES; | |
| 348 } | |
| 349 | |
| 350 // Called when the size of the window content area has changed. Override to | |
| 351 // position specific views. Base class implementation does nothing. | |
| 352 - (void)layoutSubviews { | |
| 353 NOTIMPLEMENTED(); | |
| 354 } | |
| 355 | |
| 356 @end | |
| OLD | NEW |