diff options
Diffstat (limited to 'chrome/browser/cocoa/tab_window_controller.mm')
-rw-r--r-- | chrome/browser/cocoa/tab_window_controller.mm | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/chrome/browser/cocoa/tab_window_controller.mm b/chrome/browser/cocoa/tab_window_controller.mm new file mode 100644 index 0000000..c1701f1 --- /dev/null +++ b/chrome/browser/cocoa/tab_window_controller.mm @@ -0,0 +1,342 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "chrome/browser/cocoa/tab_window_controller.h" + +#include "app/theme_provider.h" +#include "base/logging.h" +#import "chrome/browser/cocoa/tab_strip_view.h" +#import "chrome/browser/cocoa/themed_window.h" + +@interface TabWindowController(PRIVATE) +- (void)setUseOverlay:(BOOL)useOverlay; +@end + +@interface TabWindowOverlayWindow : NSWindow +@end + +@implementation TabWindowOverlayWindow + +- (ThemeProvider*)themeProvider { + if ([self parentWindow]) + return [[[self parentWindow] windowController] themeProvider]; + return NULL; +} + +- (ThemedWindowStyle)themedWindowStyle { + if ([self parentWindow]) + return [[[self parentWindow] windowController] themedWindowStyle]; + return NO; +} + +- (NSPoint)themePatternPhase { + if ([self parentWindow]) + return [[[self parentWindow] windowController] themePatternPhase]; + return NSZeroPoint; +} + +@end + +@implementation TabWindowController +@synthesize tabContentArea = tabContentArea_; + +- (id)initWithWindow:(NSWindow*)window { + if ((self = [super initWithWindow:window]) != nil) { + lockedTabs_.reset([[NSMutableSet alloc] initWithCapacity:10]); + } + return self; +} + +// Add the side tab strip to the left side of the window's content area, +// making it fill the full height of the content area. +- (void)addSideTabStripToWindow { + NSView* contentView = [[self window] contentView]; + NSRect contentFrame = [contentView frame]; + NSRect sideStripFrame = + NSMakeRect(0, 0, + NSWidth([sideTabStripView_ frame]), + NSHeight(contentFrame)); + [sideTabStripView_ setFrame:sideStripFrame]; + [contentView addSubview:sideTabStripView_]; +} + +// Add the top tab strop to the window, above the content box and add it to the +// view hierarchy as a sibling of the content view so it can overlap with the +// window frame. +- (void)addTopTabStripToWindow { + NSRect contentFrame = [tabContentArea_ frame]; + NSRect tabFrame = + NSMakeRect(0, NSMaxY(contentFrame), + NSWidth(contentFrame), + NSHeight([topTabStripView_ frame])); + [topTabStripView_ setFrame:tabFrame]; + NSView* contentParent = [[[self window] contentView] superview]; + [contentParent addSubview:topTabStripView_]; +} + +- (void)windowDidLoad { + // Cache the difference in height between the window content area and the + // tab content area. + NSRect tabFrame = [tabContentArea_ frame]; + NSRect contentFrame = [[[self window] contentView] frame]; + contentAreaHeightDelta_ = NSHeight(contentFrame) - NSHeight(tabFrame); + + if ([self hasTabStrip]) { + if ([self useVerticalTabs]) { + // No top tabstrip so remove the tabContentArea offset. + tabFrame.size.height = contentFrame.size.height; + [tabContentArea_ setFrame:tabFrame]; + [self addSideTabStripToWindow]; + } else { + [self addTopTabStripToWindow]; + } + } else { + // No top tabstrip so remove the tabContentArea offset. + tabFrame.size.height = contentFrame.size.height; + [tabContentArea_ setFrame:tabFrame]; + } +} + +// Toggles from one display mode of the tab strip to another. Will automatically +// call -layoutSubviews to reposition other content. +- (void)toggleTabStripDisplayMode { + // Adjust the size of the tab contents to either use more or less space, + // depending on the direction of the toggle. This needs to be done prior to + // adding back in the top tab strip as its position is based off the MaxY + // of the tab content area. + BOOL useVertical = [self useVerticalTabs]; + NSRect tabContentsFrame = [tabContentArea_ frame]; + tabContentsFrame.size.height += useVertical ? + contentAreaHeightDelta_ : -contentAreaHeightDelta_; + [tabContentArea_ setFrame:tabContentsFrame]; + + if (useVertical) { + // Remove the top tab strip and add the sidebar in. + [topTabStripView_ removeFromSuperview]; + [self addSideTabStripToWindow]; + } else { + // Remove the side tab strip and add the top tab strip as a sibling of the + // window's content area. + [sideTabStripView_ removeFromSuperview]; + NSRect tabContentsFrame = [tabContentArea_ frame]; + tabContentsFrame.size.height -= contentAreaHeightDelta_; + [tabContentArea_ setFrame:tabContentsFrame]; + [self addTopTabStripToWindow]; + } + + [self layoutSubviews]; +} + +// Return the appropriate tab strip based on whether or not side tabs are +// enabled. +- (TabStripView*)tabStripView { + if ([self useVerticalTabs]) + return sideTabStripView_; + return topTabStripView_; +} + +- (void)removeOverlay { + [self setUseOverlay:NO]; + if (closeDeferred_) { + // See comment in BrowserWindowCocoa::Close() about orderOut:. + [[self window] orderOut:self]; + [[self window] performClose:self]; // Autoreleases the controller. + } +} + +- (void)showOverlay { + [self setUseOverlay:YES]; +} + +// if |useOverlay| is true, we're moving views into the overlay's content +// area. If false, we're moving out of the overlay back into the window's +// content. +- (void)moveViewsBetweenWindowAndOverlay:(BOOL)useOverlay { + if (useOverlay) { + [[[overlayWindow_ contentView] superview] addSubview:[self tabStripView]]; + // Add the original window's content view as a subview of the overlay + // window's content view. We cannot simply use setContentView: here because + // the overlay window has a different content size (due to it being + // borderless). + [[overlayWindow_ contentView] addSubview:cachedContentView_]; + } else { + [[self window] setContentView:cachedContentView_]; + // The TabStripView always needs to be in front of the window's content + // view and therefore it should always be added after the content view is + // set. + [[[[self window] contentView] superview] addSubview:[self tabStripView]]; + [[[[self window] contentView] superview] updateTrackingAreas]; + } +} + +// If |useOverlay| is YES, creates a new overlay window and puts the tab strip +// and the content area inside of it. This allows it to have a different opacity +// from the title bar. If NO, returns everything to the previous state and +// destroys the overlay window until it's needed again. The tab strip and window +// contents are returned to the original window. +- (void)setUseOverlay:(BOOL)useOverlay { + [NSObject cancelPreviousPerformRequestsWithTarget:self + selector:@selector(removeOverlay) + object:nil]; + NSWindow* window = [self window]; + if (useOverlay && !overlayWindow_) { + DCHECK(!cachedContentView_); + overlayWindow_ = [[TabWindowOverlayWindow alloc] + initWithContentRect:[window frame] + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:YES]; + [overlayWindow_ setTitle:@"overlay"]; + [overlayWindow_ setBackgroundColor:[NSColor clearColor]]; + [overlayWindow_ setOpaque:NO]; + [overlayWindow_ setDelegate:self]; + cachedContentView_ = [window contentView]; + [window addChildWindow:overlayWindow_ ordered:NSWindowAbove]; + [self moveViewsBetweenWindowAndOverlay:useOverlay]; + [overlayWindow_ orderFront:nil]; + } else if (!useOverlay && overlayWindow_) { + DCHECK(cachedContentView_); + [window setContentView:cachedContentView_]; + [self moveViewsBetweenWindowAndOverlay:useOverlay]; + [window makeFirstResponder:cachedContentView_]; + [window display]; + [window removeChildWindow:overlayWindow_]; + [overlayWindow_ orderOut:nil]; + [overlayWindow_ release]; + overlayWindow_ = nil; + cachedContentView_ = nil; + } else { + NOTREACHED(); + } +} + +- (NSWindow*)overlayWindow { + return overlayWindow_; +} + +- (BOOL)shouldConstrainFrameRect { + // If we currently have an overlay window, do not attempt to change the + // window's size, as our overlay window doesn't know how to resize properly. + return overlayWindow_ == nil; +} + +- (BOOL)canReceiveFrom:(TabWindowController*)source { + // subclass must implement + NOTIMPLEMENTED(); + return NO; +} + +- (void)moveTabView:(NSView*)view + fromController:(TabWindowController*)dragController { + NOTIMPLEMENTED(); +} + +- (NSView*)selectedTabView { + NOTIMPLEMENTED(); + return nil; +} + +- (void)layoutTabs { + // subclass must implement + NOTIMPLEMENTED(); +} + +- (TabWindowController*)detachTabToNewWindow:(TabView*)tabView { + // subclass must implement + NOTIMPLEMENTED(); + return NULL; +} + +- (void)insertPlaceholderForTab:(TabView*)tab + frame:(NSRect)frame + yStretchiness:(CGFloat)yStretchiness { + [self showNewTabButton:NO]; +} + +- (void)removePlaceholder { + [self showNewTabButton:YES]; +} + +- (BOOL)tabDraggingAllowed { + return YES; +} + +- (BOOL)tabTearingAllowed { + return YES; +} + +- (BOOL)windowMovementAllowed { + return YES; +} + +- (BOOL)isTabFullyVisible:(TabView*)tab { + // Subclasses should implement this, but it's not necessary. + return YES; +} + +- (void)showNewTabButton:(BOOL)show { + // subclass must implement + NOTIMPLEMENTED(); +} + +- (void)detachTabView:(NSView*)view { + // subclass must implement + NOTIMPLEMENTED(); +} + +- (NSInteger)numberOfTabs { + // subclass must implement + NOTIMPLEMENTED(); + return 0; +} + +- (BOOL)hasLiveTabs { + // subclass must implement + NOTIMPLEMENTED(); + return NO; +} + +- (NSString*)selectedTabTitle { + // subclass must implement + NOTIMPLEMENTED(); + return @""; +} + +- (BOOL)hasTabStrip { + // Subclasses should implement this. + NOTIMPLEMENTED(); + return YES; +} + +- (BOOL)useVerticalTabs { + // Subclasses should implement this. + NOTIMPLEMENTED(); + return NO; +} + +- (BOOL)isTabDraggable:(NSView*)tabView { + return ![lockedTabs_ containsObject:tabView]; +} + +- (void)setTab:(NSView*)tabView isDraggable:(BOOL)draggable { + if (draggable) + [lockedTabs_ removeObject:tabView]; + else + [lockedTabs_ addObject:tabView]; +} + +// Tell the window that it needs to call performClose: as soon as the current +// drag is complete. This prevents a window (and its overlay) from going away +// during a drag. +- (void)deferPerformClose { + closeDeferred_ = YES; +} + +// Called when the size of the window content area has changed. Override to +// position specific views. Base class implementation does nothing. +- (void)layoutSubviews { + NOTIMPLEMENTED(); +} + +@end |