| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "chrome/browser/ui/cocoa/dev_tools_controller.h" | 5 #import "chrome/browser/ui/cocoa/dev_tools_controller.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <cmath> |
| 8 | 9 |
| 9 #include <Cocoa/Cocoa.h> | 10 #include <Cocoa/Cocoa.h> |
| 10 | 11 |
| 11 #include "base/prefs/pref_service.h" | 12 #include "base/prefs/pref_service.h" |
| 12 #include "chrome/browser/browser_process.h" | 13 #include "chrome/browser/browser_process.h" |
| 13 #include "chrome/browser/profiles/profile.h" | 14 #include "chrome/browser/profiles/profile.h" |
| 14 #import "chrome/browser/ui/cocoa/view_id_util.h" | 15 #import "chrome/browser/ui/cocoa/view_id_util.h" |
| 15 #include "chrome/common/pref_names.h" | 16 #include "chrome/common/pref_names.h" |
| 16 #include "content/public/browser/web_contents.h" | 17 #include "content/public/browser/web_contents.h" |
| 17 #include "content/public/browser/web_contents_view.h" | 18 #include "content/public/browser/web_contents_view.h" |
| 18 #include "ui/base/cocoa/focus_tracker.h" | 19 #include "ui/base/cocoa/focus_tracker.h" |
| 20 #include "ui/gfx/size_conversions.h" |
| 19 | 21 |
| 20 using content::WebContents; | 22 using content::WebContents; |
| 21 | 23 |
| 22 @interface GraySplitView : NSSplitView { | 24 @interface DevToolsContainerView : NSView { |
| 23 BOOL dividerHidden_; | 25 gfx::Insets contentsInsets_; |
| 26 |
| 27 // Weak references. Ownership via -subviews. |
| 28 NSView* devToolsView_; |
| 29 NSView* contentsView_; |
| 24 } | 30 } |
| 25 | 31 |
| 26 @property(assign, nonatomic) BOOL dividerHidden; | 32 - (void)setContentsInsets:(const gfx::Insets&)insets; |
| 27 | 33 - (void)adjustSubviews; |
| 28 - (NSColor*)dividerColor; | 34 - (void)showDevTools:(NSView*)devToolsView; |
| 29 - (CGFloat)dividerThickness; | 35 - (void)hideDevTools; |
| 30 | 36 |
| 31 @end | 37 @end |
| 32 | 38 |
| 33 | 39 |
| 34 @implementation GraySplitView | 40 @implementation DevToolsContainerView |
| 35 | 41 |
| 36 @synthesize dividerHidden = dividerHidden_; | 42 - (void)setContentsInsets:(const gfx::Insets&)insets { |
| 37 | 43 contentsInsets_ = insets; |
| 38 - (NSColor*)dividerColor { | |
| 39 return [NSColor darkGrayColor]; | |
| 40 } | 44 } |
| 41 | 45 |
| 42 - (CGFloat)dividerThickness { | 46 - (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize { |
| 43 return dividerHidden_ ? 0 : [super dividerThickness]; | 47 [self adjustSubviews]; |
| 48 } |
| 49 |
| 50 - (void)showDevTools:(NSView*)devToolsView { |
| 51 NSArray* subviews = [self subviews]; |
| 52 DCHECK_EQ(1u, [subviews count]); |
| 53 contentsView_ = [subviews objectAtIndex:0]; |
| 54 devToolsView_ = devToolsView; |
| 55 // Place DevTools under contents. |
| 56 [self addSubview:devToolsView positioned:NSWindowBelow relativeTo:nil]; |
| 57 } |
| 58 |
| 59 - (void)hideDevTools { |
| 60 DCHECK_EQ(2u, [[self subviews] count]); |
| 61 [devToolsView_ removeFromSuperview]; |
| 62 contentsView_ = nil; |
| 63 devToolsView_ = nil; |
| 64 } |
| 65 |
| 66 - (void)adjustSubviews { |
| 67 if (![[self subviews] count]) |
| 68 return; |
| 69 |
| 70 if (!devToolsView_) { |
| 71 DCHECK_EQ(1u, [[self subviews] count]); |
| 72 NSView* contents = [[self subviews] objectAtIndex:0]; |
| 73 [contents setFrame:[self bounds]]; |
| 74 return; |
| 75 } |
| 76 |
| 77 DCHECK_EQ(2u, [[self subviews] count]); |
| 78 NSRect bounds = [self bounds]; |
| 79 |
| 80 [devToolsView_ setFrame:bounds]; |
| 81 |
| 82 CGFloat width = std::max(static_cast<CGFloat>(0), |
| 83 NSWidth(bounds) - contentsInsets_.width()); |
| 84 CGFloat height = std::max(static_cast<CGFloat>(0), |
| 85 NSHeight(bounds) - contentsInsets_.height()); |
| 86 CGFloat left = std::min(static_cast<CGFloat>(contentsInsets_.left()), |
| 87 NSWidth(bounds)); |
| 88 // Flip top and bottom for NSView geometry. |
| 89 CGFloat top = std::min(static_cast<CGFloat>(contentsInsets_.bottom()), |
| 90 NSHeight(bounds)); |
| 91 [contentsView_ setFrame:NSMakeRect(left, top, width, height)]; |
| 44 } | 92 } |
| 45 | 93 |
| 46 @end | 94 @end |
| 47 | 95 |
| 48 @interface DevToolsController (Private) | 96 @interface DevToolsController (Private) |
| 49 - (void)showDevToolsContainer; | 97 - (void)showDevToolsView; |
| 50 - (void)hideDevToolsContainer; | 98 - (void)hideDevToolsView; |
| 51 - (void)updateDevToolsSplitPosition; | |
| 52 @end | 99 @end |
| 53 | 100 |
| 54 | 101 |
| 55 @implementation DevToolsController | 102 @implementation DevToolsController |
| 56 | 103 |
| 57 - (id)init { | 104 - (id)init { |
| 58 if ((self = [super init])) { | 105 if ((self = [super init])) { |
| 59 splitView_.reset([[GraySplitView alloc] initWithFrame:NSZeroRect]); | 106 devToolsContainerView_.reset( |
| 60 [splitView_ setDividerStyle:NSSplitViewDividerStyleThin]; | 107 [[DevToolsContainerView alloc] initWithFrame:NSZeroRect]); |
| 61 [splitView_ setVertical:NO]; | 108 [devToolsContainerView_ |
| 62 [splitView_ setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; | 109 setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; |
| 63 [splitView_ setDelegate:self]; | |
| 64 [splitView_ setDividerHidden:NO]; | |
| 65 | |
| 66 dockSide_ = DEVTOOLS_DOCK_SIDE_BOTTOM; | |
| 67 } | 110 } |
| 68 return self; | 111 return self; |
| 69 } | 112 } |
| 70 | 113 |
| 71 - (void)dealloc { | |
| 72 [splitView_ setDelegate:nil]; | |
| 73 [super dealloc]; | |
| 74 } | |
| 75 | |
| 76 - (NSView*)view { | 114 - (NSView*)view { |
| 77 return splitView_.get(); | 115 return devToolsContainerView_.get(); |
| 78 } | |
| 79 | |
| 80 - (NSSplitView*)splitView { | |
| 81 return splitView_.get(); | |
| 82 } | 116 } |
| 83 | 117 |
| 84 - (void)updateDevToolsForWebContents:(WebContents*)contents | 118 - (void)updateDevToolsForWebContents:(WebContents*)contents |
| 85 withProfile:(Profile*)profile { | 119 withProfile:(Profile*)profile { |
| 86 DevToolsWindow* newDevToolsWindow = contents ? | 120 DevToolsWindow* newDevToolsWindow = contents ? |
| 87 DevToolsWindow::GetDockedInstanceForInspectedTab(contents) : NULL; | 121 DevToolsWindow::GetDockedInstanceForInspectedTab(contents) : NULL; |
| 88 | 122 |
| 89 // Fast return in case of the same window having same orientation. | 123 bool shouldHide = devToolsWindow_ && devToolsWindow_ != newDevToolsWindow; |
| 90 if (devToolsWindow_ == newDevToolsWindow) { | 124 bool shouldShow = newDevToolsWindow && devToolsWindow_ != newDevToolsWindow; |
| 91 if (!newDevToolsWindow || | 125 |
| 92 (newDevToolsWindow->dock_side() == dockSide_)) { | 126 if (shouldHide) |
| 93 return; | 127 [self hideDevToolsView]; |
| 94 } | 128 |
| 129 devToolsWindow_ = newDevToolsWindow; |
| 130 if (devToolsWindow_) { |
| 131 gfx::Insets insets = devToolsWindow_->GetContentsInsets(); |
| 132 devToolsWindow_->web_contents()->GetView()->SetOverlayView( |
| 133 contents->GetView(), gfx::Point(insets.left(), insets.top())); |
| 134 [devToolsContainerView_ setContentsInsets:insets]; |
| 135 } else { |
| 136 gfx::Insets zeroInsets; |
| 137 [devToolsContainerView_ setContentsInsets:zeroInsets]; |
| 95 } | 138 } |
| 96 | 139 |
| 97 // Store last used position. | 140 if (shouldShow) |
| 98 if (devToolsWindow_) { | 141 [self showDevToolsView]; |
| 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 | 142 |
| 108 if (devToolsWindow_) | 143 [devToolsContainerView_ adjustSubviews]; |
| 109 [self hideDevToolsContainer]; | 144 if (shouldHide || shouldShow) |
| 110 | 145 [[devToolsContainerView_ window] disableScreenUpdatesUntilFlush]; |
| 111 devToolsWindow_ = newDevToolsWindow; | |
| 112 | |
| 113 if (devToolsWindow_) { | |
| 114 dockSide_ = devToolsWindow_->dock_side(); | |
| 115 [self showDevToolsContainer]; | |
| 116 } | |
| 117 } | 146 } |
| 118 | 147 |
| 119 - (void)showDevToolsContainer { | 148 - (void)showDevToolsView { |
| 120 NSArray* subviews = [splitView_ subviews]; | |
| 121 DCHECK_EQ([subviews count], 1u); | |
| 122 WebContents* devToolsContents = devToolsWindow_->web_contents(); | |
| 123 focusTracker_.reset( | 149 focusTracker_.reset( |
| 124 [[FocusTracker alloc] initWithWindow:[splitView_ window]]); | 150 [[FocusTracker alloc] initWithWindow:[devToolsContainerView_ window]]); |
| 125 | 151 |
| 126 // |devToolsView| is a TabContentsViewCocoa object, whose ViewID was | 152 // |devToolsView| is a WebContentsViewCocoa object, whose ViewID was |
| 127 // set to VIEW_ID_TAB_CONTAINER initially, so we need to change it to | 153 // set to VIEW_ID_TAB_CONTAINER initially, so we need to change it to |
| 128 // VIEW_ID_DEV_TOOLS_DOCKED here. | 154 // VIEW_ID_DEV_TOOLS_DOCKED here. |
| 129 NSView* devToolsView = devToolsContents->GetView()->GetNativeView(); | 155 NSView* devToolsView = |
| 156 devToolsWindow_->web_contents()->GetView()->GetNativeView(); |
| 130 view_id_util::SetID(devToolsView, VIEW_ID_DEV_TOOLS_DOCKED); | 157 view_id_util::SetID(devToolsView, VIEW_ID_DEV_TOOLS_DOCKED); |
| 131 [splitView_ addSubview:devToolsView]; | |
| 132 | 158 |
| 133 BOOL isVertical = devToolsWindow_->dock_side() == DEVTOOLS_DOCK_SIDE_RIGHT; | 159 [devToolsContainerView_ showDevTools:devToolsView]; |
| 134 [splitView_ setVertical:isVertical]; | |
| 135 [self updateDevToolsSplitPosition]; | |
| 136 } | 160 } |
| 137 | 161 |
| 138 - (void)hideDevToolsContainer { | 162 - (void)hideDevToolsView { |
| 139 NSArray* subviews = [splitView_ subviews]; | 163 devToolsWindow_->web_contents()->GetView()->RemoveOverlayView(); |
| 140 DCHECK_EQ([subviews count], 2u); | 164 [devToolsContainerView_ hideDevTools]; |
| 141 NSView* oldDevToolsContentsView = [subviews objectAtIndex:1]; | 165 [focusTracker_ restoreFocusInWindow:[devToolsContainerView_ window]]; |
| 142 [oldDevToolsContentsView removeFromSuperview]; | |
| 143 [splitView_ adjustSubviews]; | |
| 144 [focusTracker_ restoreFocusInWindow:[splitView_ window]]; | |
| 145 focusTracker_.reset(); | 166 focusTracker_.reset(); |
| 146 } | 167 } |
| 147 | 168 |
| 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 | 169 @end |
| OLD | NEW |