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.
5 #import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h"
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_background_view.h"
12 #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
13 #import "chrome/browser/ui/cocoa/themed_window.h"
14 #import "ui/base/cocoa/focus_tracker.h"
15 #include "ui/base/theme_provider.h"
17 @interface TabWindowController ()
18 - (void)setUseOverlay:(BOOL)useOverlay;
20 // The tab strip background view should always be inserted as the back-most
21 // subview of the root view. It cannot be a subview of the contentView, as that
22 // would cause it to become layer backed, which would cause it to draw on top
23 // of non-layer backed content like the window controls.
24 - (void)insertTabStripBackgroundViewIntoWindow:(NSWindow*)window;
27 @interface TabWindowOverlayWindow : NSWindow
30 @implementation TabWindowOverlayWindow
32 - (ui::ThemeProvider*)themeProvider {
33 if ([self parentWindow])
34 return [[[self parentWindow] windowController] themeProvider];
38 - (ThemedWindowStyle)themedWindowStyle {
39 if ([self parentWindow])
40 return [[[self parentWindow] windowController] themedWindowStyle];
44 - (NSPoint)themeImagePositionForAlignment:(ThemeImageAlignment)alignment {
45 if ([self parentWindow]) {
46 return [[[self parentWindow] windowController]
47 themeImagePositionForAlignment:alignment];
54 @implementation TabWindowController
56 - (id)initTabWindowControllerWithTabStrip:(BOOL)hasTabStrip {
57 const CGFloat kDefaultWidth = 750;
58 const CGFloat kDefaultHeight = 600;
60 NSRect contentRect = NSMakeRect(60, 229, kDefaultWidth, kDefaultHeight);
61 base::scoped_nsobject<FramedBrowserWindow> window(
62 [[FramedBrowserWindow alloc] initWithContentRect:contentRect
63 hasTabStrip:hasTabStrip]);
64 [window setReleasedWhenClosed:YES];
65 [window setAutorecalculatesKeyViewLoop:YES];
67 if ((self = [super initWithWindow:window])) {
68 [[self window] setDelegate:self];
70 chromeContentView_.reset([[NSView alloc]
71 initWithFrame:NSMakeRect(0, 0, kDefaultWidth, kDefaultHeight)]);
73 setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
74 [chromeContentView_ setWantsLayer:YES];
75 [[[self window] contentView] addSubview:chromeContentView_];
77 tabContentArea_.reset(
78 [[FastResizeView alloc] initWithFrame:[chromeContentView_ bounds]]);
79 [tabContentArea_ setAutoresizingMask:NSViewWidthSizable |
81 [chromeContentView_ addSubview:tabContentArea_];
83 // tabStripBackgroundView_ draws the theme image behind the tab strip area.
84 // When making a tab dragging window (setUseOverlay:), this view stays in
85 // the parent window so that it can be translucent, while the tab strip view
86 // moves to the child window and stays opaque.
87 NSView* windowView = [window contentView];
88 tabStripBackgroundView_.reset([[TabStripBackgroundView alloc]
89 initWithFrame:NSMakeRect(0,
90 NSMaxY([windowView bounds]) -
91 kBrowserFrameViewPaintHeight,
92 NSWidth([windowView bounds]),
93 kBrowserFrameViewPaintHeight)]);
94 [tabStripBackgroundView_
95 setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
96 [self insertTabStripBackgroundViewIntoWindow:window];
98 tabStripView_.reset([[TabStripView alloc]
99 initWithFrame:NSMakeRect(
100 0, 0, kDefaultWidth, chrome::kTabStripHeight)]);
101 [tabStripView_ setAutoresizingMask:NSViewWidthSizable |
104 [windowView addSubview:tabStripView_];
109 - (NSView*)tabStripBackgroundView {
110 return tabStripBackgroundView_;
113 - (TabStripView*)tabStripView {
114 return tabStripView_;
117 - (FastResizeView*)tabContentArea {
118 return tabContentArea_;
121 - (NSView*)chromeContentView {
122 return chromeContentView_;
125 - (void)removeOverlay {
126 [self setUseOverlay:NO];
127 if (closeDeferred_) {
128 // See comment in BrowserWindowCocoa::Close() about orderOut:.
129 [[self window] orderOut:self];
130 [[self window] performClose:self]; // Autoreleases the controller.
134 - (void)showOverlay {
135 [self setUseOverlay:YES];
138 // If |useOverlay| is YES, creates a new overlay window and puts the tab strip
139 // and the content area inside of it. This allows it to have a different opacity
140 // from the title bar. If NO, returns everything to the previous state and
141 // destroys the overlay window until it's needed again. The tab strip and window
142 // contents are returned to the original window.
143 - (void)setUseOverlay:(BOOL)useOverlay {
144 [NSObject cancelPreviousPerformRequestsWithTarget:self
145 selector:@selector(removeOverlay)
147 NSWindow* window = [self window];
148 if (useOverlay && !overlayWindow_) {
149 DCHECK(!originalContentView_);
151 overlayWindow_ = [[TabWindowOverlayWindow alloc]
152 initWithContentRect:[window frame]
153 styleMask:NSBorderlessWindowMask
154 backing:NSBackingStoreBuffered
156 [overlayWindow_ setTitle:@"overlay"];
157 [overlayWindow_ setBackgroundColor:[NSColor clearColor]];
158 [overlayWindow_ setOpaque:NO];
159 [overlayWindow_ setDelegate:self];
160 [[overlayWindow_ contentView] setWantsLayer:YES];
162 originalContentView_ = self.chromeContentView;
163 [window addChildWindow:overlayWindow_ ordered:NSWindowAbove];
165 // Explicitly set the responder to be nil here (for restoring later).
166 // If the first responder were to be left non-nil here then
167 // [RenderWidgethostViewCocoa resignFirstResponder] would be called,
168 // followed by RenderWidgetHost::Blur(), which would result in an unexpected
170 focusBeforeOverlay_.reset([[FocusTracker alloc] initWithWindow:window]);
171 [window makeFirstResponder:nil];
173 // Move the original window's tab strip view and content view to the overlay
174 // window. The content view is added as a subview of the overlay window's
175 // content view (rather than using setContentView:) because the overlay
176 // window has a different content size (due to it being borderless).
177 [[overlayWindow_ contentView] addSubview:[self tabStripView]];
178 [[overlayWindow_ contentView] addSubview:originalContentView_];
180 [overlayWindow_ orderFront:nil];
181 } else if (!useOverlay && overlayWindow_) {
182 DCHECK(originalContentView_);
184 // Return the original window's tab strip view and content view to their
185 // places. The TabStripView always needs to be in front of the window's
186 // content view and therefore it should always be added after the content
188 [[window contentView] addSubview:originalContentView_
189 positioned:NSWindowBelow
191 originalContentView_.frame = [[window contentView] bounds];
192 [[window contentView] addSubview:[self tabStripView]];
193 [[window contentView] updateTrackingAreas];
195 [focusBeforeOverlay_ restoreFocusInWindow:window];
196 focusBeforeOverlay_.reset();
199 [window removeChildWindow:overlayWindow_];
201 [overlayWindow_ orderOut:nil];
202 [overlayWindow_ release];
203 overlayWindow_ = nil;
204 originalContentView_ = nil;
210 - (NSWindow*)overlayWindow {
211 return overlayWindow_;
214 - (BOOL)shouldConstrainFrameRect {
215 // If we currently have an overlay window, do not attempt to change the
216 // window's size, as our overlay window doesn't know how to resize properly.
217 return overlayWindow_ == nil;
220 - (BOOL)canReceiveFrom:(TabWindowController*)source {
221 // subclass must implement
226 - (void)moveTabViews:(NSArray*)views
227 fromController:(TabWindowController*)dragController {
231 - (NSArray*)tabViews {
236 - (NSView*)activeTabView {
242 // subclass must implement
246 - (TabWindowController*)detachTabsToNewWindow:(NSArray*)tabViews
247 draggedTab:(NSView*)draggedTab {
248 // subclass must implement
253 - (void)insertPlaceholderForTab:(TabView*)tab frame:(NSRect)frame {
254 [self showNewTabButton:NO];
257 - (void)removePlaceholder {
258 [self showNewTabButton:YES];
261 - (BOOL)isDragSessionActive {
266 - (BOOL)tabDraggingAllowed {
270 - (BOOL)tabTearingAllowed {
274 - (BOOL)windowMovementAllowed {
278 - (BOOL)isTabFullyVisible:(TabView*)tab {
279 // Subclasses should implement this, but it's not necessary.
283 - (void)showNewTabButton:(BOOL)show {
284 // subclass must implement
288 - (void)detachTabView:(NSView*)view {
289 // subclass must implement
293 - (NSInteger)numberOfTabs {
294 // subclass must implement
299 - (BOOL)hasLiveTabs {
300 // subclass must implement
305 - (NSString*)activeTabTitle {
306 // subclass must implement
311 - (BOOL)hasTabStrip {
312 // Subclasses should implement this.
317 - (BOOL)isTabDraggable:(NSView*)tabView {
318 // Subclasses should implement this.
323 // Tell the window that it needs to call performClose: as soon as the current
324 // drag is complete. This prevents a window (and its overlay) from going away
326 - (void)deferPerformClose {
327 closeDeferred_ = YES;
330 - (void)insertTabStripBackgroundViewIntoWindow:(NSWindow*)window {
331 DCHECK(tabStripBackgroundView_);
332 NSView* rootView = [[window contentView] superview];
333 [rootView addSubview:tabStripBackgroundView_
334 positioned:NSWindowBelow
338 // Called when the size of the window content area has changed. Override to
339 // position specific views. Base class implementation does nothing.
340 - (void)layoutSubviews {