1// Copyright (c) 2012 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/tabs/tab_window_controller.h" 6 7#include "base/logging.h" 8#import "chrome/browser/ui/cocoa/browser_window_layout.h" 9#import "chrome/browser/ui/cocoa/fast_resize_view.h" 10#import "chrome/browser/ui/cocoa/framed_browser_window.h" 11#import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h" 12#import "chrome/browser/ui/cocoa/themed_window.h" 13#import "chrome/browser/ui/cocoa/version_independent_window.h" 14#import "ui/base/cocoa/focus_tracker.h" 15#include "ui/base/theme_provider.h" 16 17@interface TabWindowController(PRIVATE) 18- (void)setUseOverlay:(BOOL)useOverlay; 19@end 20 21@interface TabWindowOverlayWindow : NSWindow 22@end 23 24@implementation TabWindowOverlayWindow 25 26- (ui::ThemeProvider*)themeProvider { 27 if ([self parentWindow]) 28 return [[[self parentWindow] windowController] themeProvider]; 29 return NULL; 30} 31 32- (ThemedWindowStyle)themedWindowStyle { 33 if ([self parentWindow]) 34 return [[[self parentWindow] windowController] themedWindowStyle]; 35 return NO; 36} 37 38- (NSPoint)themeImagePositionForAlignment:(ThemeImageAlignment)alignment { 39 if ([self parentWindow]) { 40 return [[[self parentWindow] windowController] 41 themeImagePositionForAlignment:alignment]; 42 } 43 return NSZeroPoint; 44} 45 46@end 47 48@implementation TabWindowController 49 50- (id)initTabWindowControllerWithTabStrip:(BOOL)hasTabStrip { 51 NSRect contentRect = NSMakeRect(60, 229, 750, 600); 52 base::scoped_nsobject<FramedBrowserWindow> window( 53 [[FramedBrowserWindow alloc] initWithContentRect:contentRect 54 hasTabStrip:hasTabStrip]); 55 [self moveContentViewToBack:[window contentView]]; 56 [window setReleasedWhenClosed:YES]; 57 [window setAutorecalculatesKeyViewLoop:YES]; 58 59 if ((self = [super initWithWindow:window])) { 60 [[self window] setDelegate:self]; 61 62 tabContentArea_.reset([[FastResizeView alloc] initWithFrame: 63 NSMakeRect(0, 0, 750, 600)]); 64 [tabContentArea_ setAutoresizingMask:NSViewWidthSizable | 65 NSViewHeightSizable]; 66 [[[self window] contentView] addSubview:tabContentArea_]; 67 68 tabStripView_.reset([[TabStripView alloc] 69 initWithFrame:NSMakeRect(0, 0, 750, chrome::kTabStripHeight)]); 70 [tabStripView_ setAutoresizingMask:NSViewWidthSizable | 71 NSViewMinYMargin]; 72 if (hasTabStrip) 73 [self insertTabStripView:tabStripView_ intoWindow:[self window]]; 74 } 75 return self; 76} 77 78- (TabStripView*)tabStripView { 79 return tabStripView_; 80} 81 82- (FastResizeView*)tabContentArea { 83 return tabContentArea_; 84} 85 86- (void)removeOverlay { 87 [self setUseOverlay:NO]; 88 if (closeDeferred_) { 89 // See comment in BrowserWindowCocoa::Close() about orderOut:. 90 [[self window] orderOut:self]; 91 [[self window] performClose:self]; // Autoreleases the controller. 92 } 93} 94 95- (void)showOverlay { 96 [self setUseOverlay:YES]; 97} 98 99// If |useOverlay| is YES, creates a new overlay window and puts the tab strip 100// and the content area inside of it. This allows it to have a different opacity 101// from the title bar. If NO, returns everything to the previous state and 102// destroys the overlay window until it's needed again. The tab strip and window 103// contents are returned to the original window. 104- (void)setUseOverlay:(BOOL)useOverlay { 105 [NSObject cancelPreviousPerformRequestsWithTarget:self 106 selector:@selector(removeOverlay) 107 object:nil]; 108 NSWindow* window = [self window]; 109 if (useOverlay && !overlayWindow_) { 110 DCHECK(!originalContentView_); 111 112 overlayWindow_ = [[TabWindowOverlayWindow alloc] 113 initWithContentRect:[window frame] 114 styleMask:NSBorderlessWindowMask 115 backing:NSBackingStoreBuffered 116 defer:YES]; 117 [overlayWindow_ setTitle:@"overlay"]; 118 [overlayWindow_ setBackgroundColor:[NSColor clearColor]]; 119 [overlayWindow_ setOpaque:NO]; 120 [overlayWindow_ setDelegate:self]; 121 122 originalContentView_ = [window contentView]; 123 [window addChildWindow:overlayWindow_ ordered:NSWindowAbove]; 124 125 // Explicitly set the responder to be nil here (for restoring later). 126 // If the first responder were to be left non-nil here then 127 // [RenderWidgethostViewCocoa resignFirstResponder] would be called, 128 // followed by RenderWidgetHost::Blur(), which would result in an unexpected 129 // loss of focus. 130 focusBeforeOverlay_.reset([[FocusTracker alloc] initWithWindow:window]); 131 [window makeFirstResponder:nil]; 132 133 // Move the original window's tab strip view and content view to the overlay 134 // window. The content view is added as a subview of the overlay window's 135 // content view (rather than using setContentView:) because the overlay 136 // window has a different content size (due to it being borderless). 137 [[overlayWindow_ cr_windowView] addSubview:[self tabStripView]]; 138 [[overlayWindow_ contentView] addSubview:originalContentView_]; 139 140 [overlayWindow_ orderFront:nil]; 141 } else if (!useOverlay && overlayWindow_) { 142 DCHECK(originalContentView_); 143 144 // Return the original window's tab strip view and content view to their 145 // places. The TabStripView always needs to be in front of the window's 146 // content view and therefore it should always be added after the content 147 // view is set. 148 [window setContentView:originalContentView_]; 149 [self moveContentViewToBack:originalContentView_]; 150 [self insertTabStripView:[self tabStripView] intoWindow:window]; 151 [[window cr_windowView] updateTrackingAreas]; 152 153 [focusBeforeOverlay_ restoreFocusInWindow:window]; 154 focusBeforeOverlay_.reset(); 155 156 [window display]; 157 [window removeChildWindow:overlayWindow_]; 158 159 [overlayWindow_ orderOut:nil]; 160 [overlayWindow_ release]; 161 overlayWindow_ = nil; 162 originalContentView_ = nil; 163 } else { 164 NOTREACHED(); 165 } 166} 167 168- (NSWindow*)overlayWindow { 169 return overlayWindow_; 170} 171 172- (BOOL)shouldConstrainFrameRect { 173 // If we currently have an overlay window, do not attempt to change the 174 // window's size, as our overlay window doesn't know how to resize properly. 175 return overlayWindow_ == nil; 176} 177 178- (BOOL)canReceiveFrom:(TabWindowController*)source { 179 // subclass must implement 180 NOTIMPLEMENTED(); 181 return NO; 182} 183 184- (void)moveTabViews:(NSArray*)views 185 fromController:(TabWindowController*)dragController { 186 NOTIMPLEMENTED(); 187} 188 189- (NSArray*)tabViews { 190 NOTIMPLEMENTED(); 191 return nil; 192} 193 194- (NSView*)activeTabView { 195 NOTIMPLEMENTED(); 196 return nil; 197} 198 199- (void)layoutTabs { 200 // subclass must implement 201 NOTIMPLEMENTED(); 202} 203 204- (TabWindowController*)detachTabsToNewWindow:(NSArray*)tabViews 205 draggedTab:(NSView*)draggedTab { 206 // subclass must implement 207 NOTIMPLEMENTED(); 208 return NULL; 209} 210 211- (void)insertPlaceholderForTab:(TabView*)tab frame:(NSRect)frame { 212 [self showNewTabButton:NO]; 213} 214 215- (void)removePlaceholder { 216 [self showNewTabButton:YES]; 217} 218 219- (BOOL)isDragSessionActive { 220 NOTIMPLEMENTED(); 221 return NO; 222} 223 224- (BOOL)tabDraggingAllowed { 225 return YES; 226} 227 228- (BOOL)tabTearingAllowed { 229 return YES; 230} 231 232- (BOOL)windowMovementAllowed { 233 return YES; 234} 235 236- (BOOL)isTabFullyVisible:(TabView*)tab { 237 // Subclasses should implement this, but it's not necessary. 238 return YES; 239} 240 241- (void)showNewTabButton:(BOOL)show { 242 // subclass must implement 243 NOTIMPLEMENTED(); 244} 245 246- (void)detachTabView:(NSView*)view { 247 // subclass must implement 248 NOTIMPLEMENTED(); 249} 250 251- (NSInteger)numberOfTabs { 252 // subclass must implement 253 NOTIMPLEMENTED(); 254 return 0; 255} 256 257- (BOOL)hasLiveTabs { 258 // subclass must implement 259 NOTIMPLEMENTED(); 260 return NO; 261} 262 263- (NSString*)activeTabTitle { 264 // subclass must implement 265 NOTIMPLEMENTED(); 266 return @""; 267} 268 269- (BOOL)hasTabStrip { 270 // Subclasses should implement this. 271 NOTIMPLEMENTED(); 272 return YES; 273} 274 275- (BOOL)isTabDraggable:(NSView*)tabView { 276 // Subclasses should implement this. 277 NOTIMPLEMENTED(); 278 return YES; 279} 280 281// Tell the window that it needs to call performClose: as soon as the current 282// drag is complete. This prevents a window (and its overlay) from going away 283// during a drag. 284- (void)deferPerformClose { 285 closeDeferred_ = YES; 286} 287 288- (void)moveContentViewToBack:(NSView*)cv { 289 base::scoped_nsobject<NSView> contentView([cv retain]); 290 NSView* superview = [contentView superview]; 291 [contentView removeFromSuperview]; 292 [superview addSubview:contentView positioned:NSWindowBelow relativeTo:nil]; 293} 294 295- (void)insertTabStripView:(NSView*)tabStripView intoWindow:(NSWindow*)window { 296 NSView* contentParent = [window cr_windowView]; 297 if (contentParent == [[window contentView] superview]) { 298 // Add the tab strip directly above the content view, if they are siblings. 299 [contentParent addSubview:tabStripView 300 positioned:NSWindowAbove 301 relativeTo:[window contentView]]; 302 } else { 303 [contentParent addSubview:tabStripView]; 304 } 305} 306 307// Called when the size of the window content area has changed. Override to 308// position specific views. Base class implementation does nothing. 309- (void)layoutSubviews { 310 NOTIMPLEMENTED(); 311} 312 313@end 314