summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/tab_window_controller.mm
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/cocoa/tab_window_controller.mm')
-rw-r--r--chrome/browser/cocoa/tab_window_controller.mm342
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