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 |