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