| 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/framed_browser_window.h" | 5 #import "chrome/browser/ui/cocoa/framed_browser_window.h" |
| 6 | 6 |
| 7 #include <math.h> |
| 8 #include <objc/runtime.h> |
| 7 #include <stddef.h> | 9 #include <stddef.h> |
| 8 | 10 |
| 9 #include "base/logging.h" | 11 #include "base/logging.h" |
| 10 #include "base/mac/sdk_forward_declarations.h" | 12 #include "base/mac/sdk_forward_declarations.h" |
| 11 #include "chrome/browser/global_keyboard_shortcuts_mac.h" | 13 #include "chrome/browser/global_keyboard_shortcuts_mac.h" |
| 12 #include "chrome/browser/profiles/profile_avatar_icon_util.h" | 14 #include "chrome/browser/profiles/profile_avatar_icon_util.h" |
| 13 #include "chrome/browser/themes/theme_properties.h" | 15 #include "chrome/browser/themes/theme_properties.h" |
| 14 #include "chrome/browser/themes/theme_service.h" | 16 #include "chrome/browser/themes/theme_service.h" |
| 15 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 17 #import "chrome/browser/ui/cocoa/browser_window_controller.h" |
| 18 #import "chrome/browser/ui/cocoa/browser_window_layout.h" |
| 16 #import "chrome/browser/ui/cocoa/browser_window_utils.h" | 19 #import "chrome/browser/ui/cocoa/browser_window_utils.h" |
| 17 #include "chrome/browser/ui/cocoa/l10n_util.h" | 20 #include "chrome/browser/ui/cocoa/l10n_util.h" |
| 18 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h" | 21 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h" |
| 19 #import "chrome/browser/ui/cocoa/themed_window.h" | 22 #import "chrome/browser/ui/cocoa/themed_window.h" |
| 20 #include "chrome/grit/theme_resources.h" | 23 #include "chrome/grit/theme_resources.h" |
| 21 #include "ui/base/cocoa/cocoa_base_utils.h" | 24 #include "ui/base/cocoa/cocoa_base_utils.h" |
| 22 #include "ui/base/cocoa/nsgraphics_context_additions.h" | 25 #include "ui/base/cocoa/nsgraphics_context_additions.h" |
| 23 #import "ui/base/cocoa/nsview_additions.h" | 26 #import "ui/base/cocoa/nsview_additions.h" |
| 24 #include "ui/base/material_design/material_design_controller.h" | 27 #include "ui/base/material_design/material_design_controller.h" |
| 25 | 28 |
| 26 // Implementer's note: Moving the window controls is tricky. When altering the | 29 // Implementer's note: Moving the window controls is tricky. When altering the |
| 27 // code, ensure that: | 30 // code, ensure that: |
| 28 // - accessibility hit testing works | 31 // - accessibility hit testing works |
| 29 // - the accessibility hierarchy is correct | 32 // - the accessibility hierarchy is correct |
| 30 // - close/min in the background don't bring the window forward | 33 // - close/min in the background don't bring the window forward |
| 31 // - rollover effects work correctly | 34 // - rollover effects work correctly |
| 32 | 35 |
| 33 namespace { | 36 namespace { |
| 34 | 37 |
| 35 // Size of the gradient. Empirically determined so that the gradient looks | 38 // Size of the gradient. Empirically determined so that the gradient looks |
| 36 // like what the heuristic does when there are just a few tabs. | 39 // like what the heuristic does when there are just a few tabs. |
| 37 const CGFloat kWindowGradientHeight = 24.0; | 40 const CGFloat kWindowGradientHeight = 24.0; |
| 38 | 41 |
| 39 } | 42 } |
| 40 | 43 |
| 41 @interface FramedBrowserWindow (Private) | 44 @interface FramedBrowserWindow (Private) |
| 42 | 45 |
| 46 // Updates the title bar's frame so it moves the windows buttons to correct |
| 47 // location (frame bottom is moved down so the buttons are moved down as well). |
| 48 - (void)adjustTitlebarContainer:(NSView*)titlebarContainer; |
| 49 // Adds layout constraints to window buttons, respecting flag returned by |
| 50 // |ShouldDoExperimentalRTLLayout| method. |
| 51 - (void)setWindowButtonsConstraints; |
| 52 // Replaces -[NSThemeFrame addTrackingArea:] with implementation that ignores |
| 53 // tracking rect if its size is the same as the size of window buttons rect |
| 54 // (rect where close, miniaturize and zoom buttons are located). This is |
| 55 // needed to workaround macOS bug (rdar://28535344) which unnecessarily adds |
| 56 // window buttons tracking rect even if those buttons were moved. |
| 57 // TODO(crbug.com/651287): Remove this workaround once macOS bug is fixed. |
| 58 - (void)forbidAddingWindowButtonsTrackingArea; |
| 59 // Called when titlebar container changes its frame. This method adjusts |
| 60 // titlebar container with correct frame. |
| 61 - (void)titlebarDidChangeFrameNotification:(NSNotification*)notification; |
| 62 // Adds layout constraints to the given window button so it displayed at correct |
| 63 // location. This respects flag returned by |ShouldDoExperimentalRTLLayout| |
| 64 // method. |
| 65 - (void)setLeadingOffset:(CGFloat)leadingOffset |
| 66 toButton:(NSWindowButton)buttonType; |
| 67 |
| 43 - (void)adjustCloseButton:(NSNotification*)notification; | 68 - (void)adjustCloseButton:(NSNotification*)notification; |
| 44 - (void)adjustMiniaturizeButton:(NSNotification*)notification; | 69 - (void)adjustMiniaturizeButton:(NSNotification*)notification; |
| 45 - (void)adjustZoomButton:(NSNotification*)notification; | 70 - (void)adjustZoomButton:(NSNotification*)notification; |
| 46 - (void)adjustButton:(NSButton*)button | 71 - (void)adjustButton:(NSButton*)button |
| 47 ofKind:(NSWindowButton)kind; | 72 ofKind:(NSWindowButton)kind; |
| 48 - (void)childWindowsDidChange; | 73 - (void)childWindowsDidChange; |
| 49 | 74 |
| 50 @end | 75 @end |
| 51 | 76 |
| 52 @implementation FramedBrowserWindow | 77 @implementation FramedBrowserWindow |
| 53 | 78 |
| 79 + (CGFloat)browserFrameViewPaintHeight { |
| 80 return chrome::ShouldUseFullSizeContentView() ? chrome::kTabStripHeight |
| 81 : 60.0; |
| 82 } |
| 83 |
| 54 - (void)setStyleMask:(NSUInteger)styleMask { | 84 - (void)setStyleMask:(NSUInteger)styleMask { |
| 55 if (styleMaskLock_) | 85 if (styleMaskLock_) |
| 56 return; | 86 return; |
| 57 [super setStyleMask:styleMask]; | 87 [super setStyleMask:styleMask]; |
| 58 } | 88 } |
| 59 | 89 |
| 60 - (id)initWithContentRect:(NSRect)contentRect | 90 - (id)initWithContentRect:(NSRect)contentRect |
| 61 hasTabStrip:(BOOL)hasTabStrip{ | 91 hasTabStrip:(BOOL)hasTabStrip{ |
| 62 NSUInteger styleMask = NSTitledWindowMask | | 92 NSUInteger styleMask = NSTitledWindowMask | |
| 63 NSClosableWindowMask | | 93 NSClosableWindowMask | |
| 64 NSMiniaturizableWindowMask | | 94 NSMiniaturizableWindowMask | |
| 65 NSResizableWindowMask | | 95 NSResizableWindowMask | |
| 66 NSTexturedBackgroundWindowMask; | 96 NSTexturedBackgroundWindowMask; |
| 97 bool shouldUseFullSizeContentView = |
| 98 chrome::ShouldUseFullSizeContentView() && hasTabStrip; |
| 99 if (shouldUseFullSizeContentView) { |
| 100 styleMask |= NSFullSizeContentViewWindowMask; |
| 101 } |
| 102 |
| 67 if ((self = [super initWithContentRect:contentRect | 103 if ((self = [super initWithContentRect:contentRect |
| 68 styleMask:styleMask | 104 styleMask:styleMask |
| 69 backing:NSBackingStoreBuffered | 105 backing:NSBackingStoreBuffered |
| 70 defer:YES | 106 defer:YES |
| 71 wantsViewsOverTitlebar:hasTabStrip])) { | 107 wantsViewsOverTitlebar:hasTabStrip])) { |
| 72 // The 10.6 fullscreen code copies the title to a different window, which | 108 // The 10.6 fullscreen code copies the title to a different window, which |
| 73 // will assert if it's nil. | 109 // will assert if it's nil. |
| 74 [self setTitle:@""]; | 110 [self setTitle:@""]; |
| 75 | 111 |
| 76 // The following two calls fix http://crbug.com/25684 by preventing the | 112 // The following two calls fix http://crbug.com/25684 by preventing the |
| 77 // window from recalculating the border thickness as the window is | 113 // window from recalculating the border thickness as the window is |
| 78 // resized. | 114 // resized. |
| 79 // This was causing the window tint to change for the default system theme | 115 // This was causing the window tint to change for the default system theme |
| 80 // when the window was being resized. | 116 // when the window was being resized. |
| 81 [self setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge]; | 117 [self setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge]; |
| 82 [self setContentBorderThickness:kWindowGradientHeight forEdge:NSMaxYEdge]; | 118 [self setContentBorderThickness:kWindowGradientHeight forEdge:NSMaxYEdge]; |
| 83 | 119 |
| 84 hasTabStrip_ = hasTabStrip; | 120 hasTabStrip_ = hasTabStrip; |
| 85 closeButton_ = [self standardWindowButton:NSWindowCloseButton]; | 121 closeButton_ = [self standardWindowButton:NSWindowCloseButton]; |
| 86 [closeButton_ setPostsFrameChangedNotifications:YES]; | |
| 87 miniaturizeButton_ = [self standardWindowButton:NSWindowMiniaturizeButton]; | 122 miniaturizeButton_ = [self standardWindowButton:NSWindowMiniaturizeButton]; |
| 88 [miniaturizeButton_ setPostsFrameChangedNotifications:YES]; | |
| 89 zoomButton_ = [self standardWindowButton:NSWindowZoomButton]; | 123 zoomButton_ = [self standardWindowButton:NSWindowZoomButton]; |
| 90 [zoomButton_ setPostsFrameChangedNotifications:YES]; | |
| 91 | 124 |
| 92 windowButtonsInterButtonSpacing_ = | 125 windowButtonsInterButtonSpacing_ = |
| 93 NSMinX([miniaturizeButton_ frame]) - NSMaxX([closeButton_ frame]); | 126 NSMinX([miniaturizeButton_ frame]) - NSMaxX([closeButton_ frame]); |
| 94 | 127 |
| 95 [self adjustButton:closeButton_ ofKind:NSWindowCloseButton]; | 128 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
| 96 [self adjustButton:miniaturizeButton_ ofKind:NSWindowMiniaturizeButton]; | 129 if (shouldUseFullSizeContentView) { |
| 97 [self adjustButton:zoomButton_ ofKind:NSWindowZoomButton]; | 130 // If Chrome uses full sized content view then window buttons are placed |
| 131 // inside titlebar (which height is 22 points). In order to move window |
| 132 // buttons down the whole toolbar should be moved down. |
| 133 DCHECK(closeButton_); |
| 134 NSView* titlebarContainer = [[closeButton_ superview] superview]; |
| 135 [self adjustTitlebarContainer:titlebarContainer]; |
| 136 [center addObserver:self |
| 137 selector:@selector(titlebarDidChangeFrameNotification:) |
| 138 name:NSViewFrameDidChangeNotification |
| 139 object:titlebarContainer]; |
| 140 // Window buttons are not movable unless their positioning is forced via |
| 141 // layout constraints. |
| 142 [self setWindowButtonsConstraints]; |
| 143 // Remove an extra tracking rect unnecessarily added by AppKit which |
| 144 // highlights the buttons on mouse enter event. That rect is added where |
| 145 // buttons used to be previously. |
| 146 [self forbidAddingWindowButtonsTrackingArea]; |
| 147 } else { |
| 148 // If Chrome does not use a full sized content view then AppKit adds the |
| 149 // window buttons to the root view, where they must be manually |
| 150 // re-positioned. |
| 151 [self adjustButton:closeButton_ ofKind:NSWindowCloseButton]; |
| 152 [self adjustButton:miniaturizeButton_ ofKind:NSWindowMiniaturizeButton]; |
| 153 [self adjustButton:zoomButton_ ofKind:NSWindowZoomButton]; |
| 154 [closeButton_ setPostsFrameChangedNotifications:YES]; |
| 155 [miniaturizeButton_ setPostsFrameChangedNotifications:YES]; |
| 156 [zoomButton_ setPostsFrameChangedNotifications:YES]; |
| 98 | 157 |
| 99 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; | 158 [center addObserver:self |
| 100 [center addObserver:self | 159 selector:@selector(adjustCloseButton:) |
| 101 selector:@selector(adjustCloseButton:) | 160 name:NSViewFrameDidChangeNotification |
| 102 name:NSViewFrameDidChangeNotification | 161 object:closeButton_]; |
| 103 object:closeButton_]; | 162 [center addObserver:self |
| 104 [center addObserver:self | 163 selector:@selector(adjustMiniaturizeButton:) |
| 105 selector:@selector(adjustMiniaturizeButton:) | 164 name:NSViewFrameDidChangeNotification |
| 106 name:NSViewFrameDidChangeNotification | 165 object:miniaturizeButton_]; |
| 107 object:miniaturizeButton_]; | 166 [center addObserver:self |
| 108 [center addObserver:self | 167 selector:@selector(adjustZoomButton:) |
| 109 selector:@selector(adjustZoomButton:) | 168 name:NSViewFrameDidChangeNotification |
| 110 name:NSViewFrameDidChangeNotification | 169 object:zoomButton_]; |
| 111 object:zoomButton_]; | 170 } |
| 112 } | 171 } |
| 113 | 172 |
| 114 return self; | 173 return self; |
| 115 } | 174 } |
| 116 | 175 |
| 117 - (void)dealloc { | 176 - (void)dealloc { |
| 118 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 177 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 119 [super dealloc]; | 178 [super dealloc]; |
| 120 } | 179 } |
| 121 | 180 |
| 181 - (void)adjustTitlebarContainer:(NSView*)titlebarContainer { |
| 182 DCHECK(chrome::ShouldUseFullSizeContentView()); |
| 183 DCHECK([NSStringFromClass([titlebarContainer class]) |
| 184 isEqual:@"NSTitlebarContainerView"]); |
| 185 |
| 186 NSRect newFrame = [titlebarContainer frame]; |
| 187 NSRect superviewFrame = [[titlebarContainer superview] frame]; |
| 188 // Increase toolbar height to move window buttons down where they should be. |
| 189 newFrame.size.height = |
| 190 floor((chrome::kTabStripHeight + NSHeight([closeButton_ frame])) / 2.0); |
| 191 newFrame.size.width = NSWidth(superviewFrame); |
| 192 newFrame.origin.y = NSHeight(superviewFrame) - NSHeight(newFrame); |
| 193 newFrame.origin.x = NSMinX(superviewFrame); |
| 194 [titlebarContainer setFrame:newFrame]; |
| 195 } |
| 196 |
| 197 - (void)setWindowButtonsConstraints { |
| 198 DCHECK(chrome::ShouldUseFullSizeContentView()); |
| 199 |
| 200 CGFloat leadingOffset = |
| 201 hasTabStrip_ ? kFramedWindowButtonsWithTabStripOffsetFromLeft |
| 202 : kFramedWindowButtonsWithoutTabStripOffsetFromLeft; |
| 203 [self setLeadingOffset:leadingOffset toButton:NSWindowCloseButton]; |
| 204 |
| 205 leadingOffset += |
| 206 windowButtonsInterButtonSpacing_ + NSWidth([closeButton_ frame]); |
| 207 [self setLeadingOffset:leadingOffset toButton:NSWindowMiniaturizeButton]; |
| 208 |
| 209 leadingOffset += |
| 210 windowButtonsInterButtonSpacing_ + NSWidth([miniaturizeButton_ frame]); |
| 211 [self setLeadingOffset:leadingOffset toButton:NSWindowZoomButton]; |
| 212 } |
| 213 |
| 214 - (void)forbidAddingWindowButtonsTrackingArea { |
| 215 DCHECK(chrome::ShouldUseFullSizeContentView()); |
| 216 |
| 217 static dispatch_once_t onceToken; |
| 218 dispatch_once(&onceToken, ^{ |
| 219 NSView* themeFrame = [[[closeButton_ superview] superview] superview]; |
| 220 Class themeFrameClass = [themeFrame class]; |
| 221 DCHECK([NSStringFromClass(themeFrameClass) isEqual:@"NSThemeFrame"]); |
| 222 SEL addTrackingAreaSelector = @selector(addTrackingArea:); |
| 223 Method originalMethod = |
| 224 class_getInstanceMethod(themeFrameClass, addTrackingAreaSelector); |
| 225 IMP originalImp = method_getImplementation(originalMethod); |
| 226 NSRect windowButtonsRect = NSUnionRect( |
| 227 NSUnionRect([closeButton_ frame], [miniaturizeButton_ frame]), |
| 228 [zoomButton_ frame]); |
| 229 NSSize buttonsAreaSize = NSIntegralRect(windowButtonsRect).size; |
| 230 |
| 231 // |newImp| is never released with |imp_removeBlock|. |
| 232 IMP newImp = imp_implementationWithBlock(^(id self, id area) { |
| 233 // There is no other way to ensure that |area| is responsible for buttons |
| 234 // highlighting except by relying on its size. |
| 235 if (!NSEqualSizes(buttonsAreaSize, NSIntegralRect([area rect]).size)) { |
| 236 originalImp(self, addTrackingAreaSelector, area); |
| 237 } |
| 238 }); |
| 239 |
| 240 // Do not use base::mac::ScopedObjCClassSwizzler as it replaces existing |
| 241 // implementation which is defined in NSView and will affect the whole app |
| 242 // performance. |
| 243 class_replaceMethod(themeFrameClass, addTrackingAreaSelector, newImp, |
| 244 method_getTypeEncoding(originalMethod)); |
| 245 }); |
| 246 } |
| 247 |
| 248 - (void)titlebarDidChangeFrameNotification:(NSNotification*)notification { |
| 249 [self adjustTitlebarContainer:[notification object]]; |
| 250 } |
| 251 |
| 252 - (void)setLeadingOffset:(CGFloat)leadingOffset |
| 253 toButton:(NSWindowButton)buttonType { |
| 254 DCHECK(chrome::ShouldUseFullSizeContentView()); |
| 255 |
| 256 NSButton* button = [self standardWindowButton:buttonType]; |
| 257 [button setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| 258 |
| 259 // Do not use leadingAnchor because |ShouldDoExperimentalRTLLayout| |
| 260 // should determine if current locale is RTL. |
| 261 NSLayoutXAxisAnchor* leadingSourceAnchor = [button leftAnchor]; |
| 262 NSLayoutXAxisAnchor* leadingTargetAnchor = [[button superview] leftAnchor]; |
| 263 if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) { |
| 264 leadingSourceAnchor = [button rightAnchor]; |
| 265 leadingTargetAnchor = [[button superview] rightAnchor]; |
| 266 leadingOffset = -leadingOffset; |
| 267 } |
| 268 [[leadingSourceAnchor constraintEqualToAnchor:leadingTargetAnchor |
| 269 constant:leadingOffset] setActive:YES]; |
| 270 } |
| 271 |
| 122 - (void)adjustCloseButton:(NSNotification*)notification { | 272 - (void)adjustCloseButton:(NSNotification*)notification { |
| 123 [self adjustButton:[notification object] | 273 [self adjustButton:[notification object] |
| 124 ofKind:NSWindowCloseButton]; | 274 ofKind:NSWindowCloseButton]; |
| 125 } | 275 } |
| 126 | 276 |
| 127 - (void)adjustMiniaturizeButton:(NSNotification*)notification { | 277 - (void)adjustMiniaturizeButton:(NSNotification*)notification { |
| 128 [self adjustButton:[notification object] | 278 [self adjustButton:[notification object] |
| 129 ofKind:NSWindowMiniaturizeButton]; | 279 ofKind:NSWindowMiniaturizeButton]; |
| 130 } | 280 } |
| 131 | 281 |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 348 [self childWindowsDidChange]; | 498 [self childWindowsDidChange]; |
| 349 } | 499 } |
| 350 | 500 |
| 351 - (void)childWindowsDidChange { | 501 - (void)childWindowsDidChange { |
| 352 id delegate = [self delegate]; | 502 id delegate = [self delegate]; |
| 353 if ([delegate respondsToSelector:@selector(childWindowsDidChange)]) | 503 if ([delegate respondsToSelector:@selector(childWindowsDidChange)]) |
| 354 [delegate childWindowsDidChange]; | 504 [delegate childWindowsDidChange]; |
| 355 } | 505 } |
| 356 | 506 |
| 357 @end | 507 @end |
| OLD | NEW |