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