1 // Copyright (c) 2012 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/dev_tools_controller.h" 6 7 #include <algorithm> 8 9 #include <Cocoa/Cocoa.h> 10 11 #include "base/prefs/pref_service.h" 12 #include "chrome/browser/browser_process.h" 13 #include "chrome/browser/profiles/profile.h" 14 #import "chrome/browser/ui/cocoa/view_id_util.h" 15 #include "chrome/common/pref_names.h" 16 #include "content/public/browser/web_contents.h" 17 #include "content/public/browser/web_contents_view.h" 18 #include "ui/base/cocoa/focus_tracker.h" 19 20 using content::WebContents; 21 22 @interface GraySplitView : NSSplitView { 23 BOOL dividerHidden_; 24 } 25 26 @property(assign, nonatomic) BOOL dividerHidden; 27 28 - (NSColor*)dividerColor; 29 - (CGFloat)dividerThickness; 30 31 @end 32 33 34 @implementation GraySplitView 35 36 @synthesize dividerHidden = dividerHidden_; 37 38 - (NSColor*)dividerColor { 39 return [NSColor darkGrayColor]; 40 } 41 42 - (CGFloat)dividerThickness { 43 return dividerHidden_ ? 0 : [super dividerThickness]; 44 } 45 46 @end 47 48 @interface DevToolsController (Private) 49 - (void)showDevToolsContainer; 50 - (void)hideDevToolsContainer; 51 - (void)updateDevToolsSplitPosition; 52 @end 53 54 55 @implementation DevToolsController 56 57 - (id)init { 58 if ((self = [super init])) { 59 splitView_.reset([[GraySplitView alloc] initWithFrame:NSZeroRect]); 60 [splitView_ setDividerStyle:NSSplitViewDividerStyleThin]; 61 [splitView_ setVertical:NO]; 62 [splitView_ setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; 63 [splitView_ setDelegate:self]; 64 [splitView_ setDividerHidden:NO]; 65 66 dockSide_ = DEVTOOLS_DOCK_SIDE_BOTTOM; 67 } 68 return self; 69 } 70 71 - (void)dealloc { 72 [splitView_ setDelegate:nil]; 73 [super dealloc]; 74 } 75 76 - (NSView*)view { 77 return splitView_.get(); 78 } 79 80 - (NSSplitView*)splitView { 81 return splitView_.get(); 82 } 83 84 - (void)updateDevToolsForWebContents:(WebContents*)contents 85 withProfile:(Profile*)profile { 86 DevToolsWindow* newDevToolsWindow = contents ? 87 DevToolsWindow::GetDockedInstanceForInspectedTab(contents) : NULL; 88 89 // Fast return in case of the same window having same orientation. 90 if (devToolsWindow_ == newDevToolsWindow) { 91 if (!newDevToolsWindow || 92 (newDevToolsWindow->dock_side() == dockSide_)) { 93 return; 94 } 95 } 96 97 // Store last used position. 98 if (devToolsWindow_) { 99 NSArray* subviews = [splitView_ subviews]; 100 DCHECK_EQ([subviews count], 2u); 101 NSView* devToolsView = [subviews objectAtIndex:1]; 102 if (dockSide_ == DEVTOOLS_DOCK_SIDE_RIGHT) 103 devToolsWindow_->SetWidth(NSWidth([devToolsView frame])); 104 else if (dockSide_ == DEVTOOLS_DOCK_SIDE_BOTTOM) 105 devToolsWindow_->SetHeight(NSHeight([devToolsView frame])); 106 } 107 108 if (devToolsWindow_) 109 [self hideDevToolsContainer]; 110 111 devToolsWindow_ = newDevToolsWindow; 112 113 if (devToolsWindow_) { 114 dockSide_ = devToolsWindow_->dock_side(); 115 [self showDevToolsContainer]; 116 } 117 } 118 119 - (void)showDevToolsContainer { 120 NSArray* subviews = [splitView_ subviews]; 121 DCHECK_EQ([subviews count], 1u); 122 WebContents* devToolsContents = devToolsWindow_->web_contents(); 123 focusTracker_.reset( 124 [[FocusTracker alloc] initWithWindow:[splitView_ window]]); 125 126 // |devToolsView| is a TabContentsViewCocoa object, whose ViewID was 127 // set to VIEW_ID_TAB_CONTAINER initially, so we need to change it to 128 // VIEW_ID_DEV_TOOLS_DOCKED here. 129 NSView* devToolsView = devToolsContents->GetView()->GetNativeView(); 130 view_id_util::SetID(devToolsView, VIEW_ID_DEV_TOOLS_DOCKED); 131 [splitView_ addSubview:devToolsView]; 132 133 BOOL isVertical = devToolsWindow_->dock_side() == DEVTOOLS_DOCK_SIDE_RIGHT; 134 [splitView_ setVertical:isVertical]; 135 [self updateDevToolsSplitPosition]; 136 } 137 138 - (void)hideDevToolsContainer { 139 NSArray* subviews = [splitView_ subviews]; 140 DCHECK_EQ([subviews count], 2u); 141 NSView* oldDevToolsContentsView = [subviews objectAtIndex:1]; 142 [oldDevToolsContentsView removeFromSuperview]; 143 [splitView_ adjustSubviews]; 144 [focusTracker_ restoreFocusInWindow:[splitView_ window]]; 145 focusTracker_.reset(); 146 } 147 148 - (void)updateDevToolsSplitPosition { 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 devtools. 153 // TODO(alekseys): either make setPosition:ofDividerAtIndex: work or to add a 154 // category on NSSplitView to handle manual resizing. 155 NSView* webView = [subviews objectAtIndex:0]; 156 NSRect webFrame = [webView frame]; 157 NSView* devToolsView = [subviews objectAtIndex:1]; 158 NSRect devToolsFrame = [devToolsView frame]; 159 160 BOOL noDivider = devToolsWindow_->dock_side() == DEVTOOLS_DOCK_SIDE_MINIMIZED; 161 [splitView_ setDividerHidden:noDivider]; 162 163 if (devToolsWindow_->dock_side() == DEVTOOLS_DOCK_SIDE_RIGHT) { 164 CGFloat size = devToolsWindow_->GetWidth(NSWidth([splitView_ frame])); 165 devToolsFrame.size.width = size; 166 webFrame.size.width = 167 NSWidth([splitView_ frame]) - ([splitView_ dividerThickness] + size); 168 } else { 169 CGFloat size = 170 devToolsWindow_->dock_side() == DEVTOOLS_DOCK_SIDE_MINIMIZED ? 171 devToolsWindow_->GetMinimizedHeight() : 172 devToolsWindow_->GetHeight(NSHeight([splitView_ frame])); 173 devToolsFrame.size.height = size; 174 webFrame.size.height = 175 NSHeight([splitView_ frame]) - ([splitView_ dividerThickness] + size); 176 } 177 178 [[splitView_ window] disableScreenUpdatesUntilFlush]; 179 [webView setFrame:webFrame]; 180 [devToolsView setFrame:devToolsFrame]; 181 182 [splitView_ adjustSubviews]; 183 } 184 185 // NSSplitViewDelegate protocol. 186 - (BOOL)splitView:(NSSplitView *)splitView 187 shouldAdjustSizeOfSubview:(NSView *)subview { 188 // Return NO for the devTools view to indicate that it should not be resized 189 // automatically. It preserves the height set by the user and also keeps 190 // view height the same while changing tabs when one of the tabs shows infobar 191 // and others are not. 192 if ([[splitView_ subviews] indexOfObject:subview] == 1) 193 return NO; 194 return YES; 195 } 196 197 - (NSRect)splitView:(NSSplitView*)splitView 198 effectiveRect:(NSRect)proposedEffectiveRect 199 forDrawnRect:(NSRect)drawnRect 200 ofDividerAtIndex:(NSInteger)dividerIndex { 201 if (devToolsWindow_->dock_side() == DEVTOOLS_DOCK_SIDE_MINIMIZED) { 202 return NSZeroRect; 203 } else { 204 return proposedEffectiveRect; 205 } 206 } 207 208 - (CGFloat)splitView:(NSSplitView*)splitView 209 constrainMaxCoordinate:(CGFloat)proposedMax 210 ofSubviewAt:(NSInteger)dividerIndex { 211 if ([splitView_ isVertical]) { 212 return NSWidth([splitView_ frame]) - [splitView_ dividerThickness] - 213 devToolsWindow_->GetMinimumWidth(); 214 } else { 215 return NSHeight([splitView_ frame]) - [splitView_ dividerThickness] - 216 devToolsWindow_->GetMinimumHeight(); 217 } 218 } 219 220 - (CGFloat)splitView:(NSSplitView *)splitView 221 constrainSplitPosition:(CGFloat)proposedPosition 222 ofSubviewAt:(NSInteger)dividerIndex { 223 return round(proposedPosition); 224 } 225 226 -(void)splitViewWillResizeSubviews:(NSNotification *)notification { 227 [[splitView_ window] disableScreenUpdatesUntilFlush]; 228 } 229 230 @end 231