sidebar_controller.mm revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
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/sidebar_controller.h"
6
7#include <algorithm>
8
9#include <Cocoa/Cocoa.h>
10
11#include "chrome/browser/browser_process.h"
12#include "chrome/browser/prefs/pref_service.h"
13#include "chrome/browser/sidebar/sidebar_manager.h"
14#include "chrome/browser/tab_contents/tab_contents.h"
15#include "chrome/browser/ui/browser.h"
16#import "chrome/browser/ui/cocoa/view_id_util.h"
17#include "chrome/common/pref_names.h"
18
19namespace {
20
21// By default sidebar width is 1/7th of the current page content width.
22const CGFloat kDefaultSidebarWidthRatio = 1.0 / 7;
23
24// Never make the web part of the tab contents smaller than this (needed if the
25// window is only a few pixels wide).
26const int kMinWebWidth = 50;
27
28}  // end namespace
29
30
31@interface SidebarController (Private)
32- (void)showSidebarContents:(TabContents*)sidebarContents;
33- (void)resizeSidebarToNewWidth:(CGFloat)width;
34@end
35
36
37@implementation SidebarController
38
39- (id)initWithDelegate:(id<TabContentsControllerDelegate>)delegate {
40  if ((self = [super init])) {
41    splitView_.reset([[NSSplitView alloc] initWithFrame:NSZeroRect]);
42    [splitView_ setDividerStyle:NSSplitViewDividerStyleThin];
43    [splitView_ setVertical:YES];
44    [splitView_ setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
45    [splitView_ setDelegate:self];
46
47    contentsController_.reset(
48        [[TabContentsController alloc] initWithContents:NULL
49                                               delegate:delegate]);
50  }
51  return self;
52}
53
54- (void)dealloc {
55  [splitView_ setDelegate:nil];
56  [super dealloc];
57}
58
59- (NSSplitView*)view {
60  return splitView_.get();
61}
62
63- (NSSplitView*)splitView {
64  return splitView_.get();
65}
66
67- (void)updateSidebarForTabContents:(TabContents*)contents {
68  // Get the active sidebar content.
69  if (SidebarManager::GetInstance() == NULL)  // Happens in tests.
70    return;
71
72  TabContents* sidebarContents = NULL;
73  if (contents && SidebarManager::IsSidebarAllowed()) {
74    SidebarContainer* activeSidebar =
75        SidebarManager::GetInstance()->GetActiveSidebarContainerFor(contents);
76    if (activeSidebar)
77      sidebarContents = activeSidebar->sidebar_contents();
78  }
79
80  TabContents* oldSidebarContents = [contentsController_ tabContents];
81  if (oldSidebarContents == sidebarContents)
82    return;
83
84  // Adjust sidebar view.
85  [self showSidebarContents:sidebarContents];
86
87  // Notify extensions.
88  SidebarManager::GetInstance()->NotifyStateChanges(
89      oldSidebarContents, sidebarContents);
90}
91
92- (void)ensureContentsVisible {
93  [contentsController_ ensureContentsVisible];
94}
95
96- (void)showSidebarContents:(TabContents*)sidebarContents {
97  [contentsController_ ensureContentsSizeDoesNotChange];
98
99  NSArray* subviews = [splitView_ subviews];
100  if (sidebarContents) {
101    DCHECK_GE([subviews count], 1u);
102
103    // Native view is a TabContentsViewCocoa object, whose ViewID was
104    // set to VIEW_ID_TAB_CONTAINER initially, so change it to
105    // VIEW_ID_SIDE_BAR_CONTAINER here.
106    view_id_util::SetID(
107        sidebarContents->GetNativeView(), VIEW_ID_SIDE_BAR_CONTAINER);
108
109    CGFloat sidebarWidth = 0;
110    if ([subviews count] == 1) {
111      // Load the default split offset.
112      sidebarWidth = g_browser_process->local_state()->GetInteger(
113          prefs::kExtensionSidebarWidth);
114      if (sidebarWidth < 0) {
115        // Initial load, set to default value.
116        sidebarWidth =
117            NSWidth([splitView_ frame]) * kDefaultSidebarWidthRatio;
118      }
119      [splitView_ addSubview:[contentsController_ view]];
120    } else {
121      DCHECK_EQ([subviews count], 2u);
122      sidebarWidth = NSWidth([[subviews objectAtIndex:1] frame]);
123    }
124
125    // Make sure |sidebarWidth| isn't too large or too small.
126    sidebarWidth = std::min(sidebarWidth,
127                            NSWidth([splitView_ frame]) - kMinWebWidth);
128    DCHECK_GE(sidebarWidth, 0) << "kMinWebWidth needs to be smaller than "
129                               << "smallest available tab contents space.";
130    sidebarWidth = std::max(static_cast<CGFloat>(0), sidebarWidth);
131
132    [self resizeSidebarToNewWidth:sidebarWidth];
133  } else {
134    if ([subviews count] > 1) {
135      NSView* oldSidebarContentsView = [subviews objectAtIndex:1];
136      // Store split offset when hiding sidebar window only.
137      int sidebarWidth = NSWidth([oldSidebarContentsView frame]);
138      g_browser_process->local_state()->SetInteger(
139          prefs::kExtensionSidebarWidth, sidebarWidth);
140      [oldSidebarContentsView removeFromSuperview];
141      [splitView_ adjustSubviews];
142    }
143  }
144
145  [contentsController_ changeTabContents:sidebarContents];
146}
147
148- (void)resizeSidebarToNewWidth:(CGFloat)width {
149  NSArray* subviews = [splitView_ subviews];
150
151  // It seems as if |-setPosition:ofDividerAtIndex:| should do what's needed,
152  // but I can't figure out how to use it. Manually resize web and sidebar.
153  // TODO(alekseys): either make setPosition:ofDividerAtIndex: work or to add a
154  // category on NSSplitView to handle manual resizing.
155  NSView* sidebarView = [subviews objectAtIndex:1];
156  NSRect sidebarFrame = [sidebarView frame];
157  sidebarFrame.size.width = width;
158  [sidebarView setFrame:sidebarFrame];
159
160  NSView* webView = [subviews objectAtIndex:0];
161  NSRect webFrame = [webView frame];
162  webFrame.size.width =
163      NSWidth([splitView_ frame]) - ([splitView_ dividerThickness] + width);
164  [webView setFrame:webFrame];
165
166  [splitView_ adjustSubviews];
167}
168
169// NSSplitViewDelegate protocol.
170- (BOOL)splitView:(NSSplitView *)splitView
171    shouldAdjustSizeOfSubview:(NSView *)subview {
172  // Return NO for the sidebar view to indicate that it should not be resized
173  // automatically.  The sidebar keeps the width set by the user.
174  if ([[splitView_ subviews] indexOfObject:subview] == 1)
175    return NO;
176  return YES;
177}
178
179@end
180