From adebcb60c6ae805b8e35640b06fa57ed37480da6 Mon Sep 17 00:00:00 2001 From: jackhou Date: Sun, 22 Feb 2015 16:39:48 -0800 Subject: [PATCH] [Mac] Use a custom view to implement colored app windows. When building with >=10.9 SDK, swizzling drawRect on the window no longer works. See https://codereview.chromium.org/611453004 This changes NativeAppWindowCocoa to put an extra view in the title bar that draws the color. BUG=457991 Review URL: https://codereview.chromium.org/916833005 Cr-Commit-Position: refs/heads/master@{#317534} --- .../ui/cocoa/apps/native_app_window_cocoa.h | 2 + .../ui/cocoa/apps/native_app_window_cocoa.mm | 112 +++++++++------------ 2 files changed, 50 insertions(+), 64 deletions(-) diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h index b8aef3d69fc3..d41d80abbeec 100644 --- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h +++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h @@ -23,6 +23,7 @@ class ExtensionKeybindingRegistryCocoa; class NativeAppWindowCocoa; @class ShellNSWindow; class SkRegion; +@class TitlebarBackgroundView; // A window controller for a minimal window to host a web app view. Passes // Objective-C notifications to the C++ bridge. @@ -206,6 +207,7 @@ class NativeAppWindowCocoa : public extensions::NativeAppWindow, SkColor inactive_frame_color_; base::scoped_nsobject window_controller_; + base::scoped_nsobject titlebar_background_view_; // For system drag, the whole window is draggable and the non-draggable areas // have to been explicitly excluded. diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm index cd978fb1db12..f8b10ce1a3c4 100644 --- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm +++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm @@ -13,7 +13,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/cocoa/browser_window_utils.h" #import "chrome/browser/ui/cocoa/chrome_event_processing_window.h" -#import "chrome/browser/ui/cocoa/custom_frame_view.h" #include "chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.h" #include "chrome/browser/ui/cocoa/extensions/extension_view_mac.h" #include "chrome/common/chrome_switches.h" @@ -208,71 +207,29 @@ std::vector CalculateNonDraggableRegions( @end -// This is really a method on NSGrayFrame, so it should only be called on the -// view passed into -[NSWindow drawCustomFrameRect:forView:]. -@interface NSView (PrivateMethods) -- (CGFloat)roundedCornerRadius; -@end - -// TODO(jamescook): Should these be AppNSWindow to match AppWindow? -// http://crbug.com/344082 -@interface ShellNSWindow : ChromeEventProcessingWindow -@end -@implementation ShellNSWindow - -// Similar to ChromeBrowserWindow, don't draw the title, but allow it to be seen -// in menus, Expose, etc. -- (BOOL)_isTitleHidden { - return YES; -} - -- (void)drawCustomFrameRect:(NSRect)frameRect forView:(NSView*)view { - // Make the background color of the content area white. We can't just call - // -setBackgroundColor as that causes the title bar to be drawn in a solid - // color. - NSRect rect = [self contentRectForFrameRect:frameRect]; - [[NSColor whiteColor] set]; - NSRectFill(rect); - - // Draw the native title bar. We remove the content area since the native - // implementation draws a gray background. - rect.origin.y = NSMaxY(rect); - rect.size.height = CGFLOAT_MAX; - rect = NSIntersectionRect(rect, frameRect); - - [NSBezierPath clipRect:rect]; - [super drawCustomFrameRect:frameRect - forView:view]; -} - -@end - -@interface ShellCustomFrameNSWindow : ShellNSWindow { +// A view that paints a solid color. Used to change the title bar background. +@interface TitlebarBackgroundView : NSView { @private base::scoped_nsobject color_; base::scoped_nsobject inactiveColor_; } - - (void)setColor:(NSColor*)color inactiveColor:(NSColor*)inactiveColor; - @end -@implementation ShellCustomFrameNSWindow +@implementation TitlebarBackgroundView -- (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view { - [[NSBezierPath bezierPathWithRect:rect] addClip]; - [[NSColor clearColor] set]; - NSRectFill(rect); - - // Set up our clip. +- (void)drawRect:(NSRect)rect { + // Only the top corners are rounded. For simplicity, round all 4 corners but + // draw the bottom corners outside of the visible bounds. CGFloat cornerRadius = 4.0; - if ([view respondsToSelector:@selector(roundedCornerRadius)]) - cornerRadius = [view roundedCornerRadius]; - [[NSBezierPath bezierPathWithRoundedRect:[view bounds] + NSRect roundedRect = [self bounds]; + roundedRect.origin.y -= cornerRadius; + roundedRect.size.height += cornerRadius; + [[NSBezierPath bezierPathWithRoundedRect:roundedRect xRadius:cornerRadius yRadius:cornerRadius] addClip]; - if ([self isMainWindow] || [self isKeyWindow]) + if ([[self window] isMainWindow] || [[self window] isKeyWindow]) [color_ set]; else [inactiveColor_ set]; @@ -287,13 +244,26 @@ std::vector CalculateNonDraggableRegions( @end +// TODO(jamescook): Should these be AppNSWindow to match AppWindow? +// http://crbug.com/344082 +@interface ShellNSWindow : ChromeEventProcessingWindow +@end + +@implementation ShellNSWindow + +// Similar to ChromeBrowserWindow, don't draw the title, but allow it to be seen +// in menus, Expose, etc. +- (BOOL)_isTitleHidden { + return YES; +} + +@end + @interface ShellFramelessNSWindow : ShellNSWindow @end @implementation ShellFramelessNSWindow -- (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view {} - + (NSRect)frameRectForContentRect:(NSRect)contentRect styleMask:(NSUInteger)mask { return contentRect; @@ -350,13 +320,8 @@ NativeAppWindowCocoa::NativeAppWindowCocoa( Observe(WebContents()); base::scoped_nsobject window; - Class window_class; - if (has_frame_) { - window_class = has_frame_color_ ? - [ShellCustomFrameNSWindow class] : [ShellNSWindow class]; - } else { - window_class = [ShellFramelessNSWindow class]; - } + Class window_class = has_frame_ ? + [ShellNSWindow class] : [ShellFramelessNSWindow class]; // Estimate the initial bounds of the window. Once the frame insets are known, // the window bounds and constraints can be set precisely. @@ -375,7 +340,23 @@ NativeAppWindowCocoa::NativeAppWindowCocoa( [window setTitle:base::SysUTF8ToNSString(name)]; [[window contentView] setWantsLayer:YES]; if (has_frame_ && has_frame_color_) { - [base::mac::ObjCCastStrict(window) + // AppKit only officially supports adding subviews to the window's + // contentView and not its superview (an NSNextStepFrame). The 10.10 SDK + // allows adding an NSTitlebarAccessoryViewController to a window, but the + // view can only be placed above the window control buttons, so we'd have to + // replicate those. + NSView* window_view = [[window contentView] superview]; + CGFloat height = NSHeight([window_view bounds]) - + NSHeight([[window contentView] bounds]); + titlebar_background_view_.reset([[TitlebarBackgroundView alloc] + initWithFrame:NSMakeRect(0, NSMaxY([window_view bounds]) - height, + NSWidth([window_view bounds]), height)]); + [titlebar_background_view_ + setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin]; + [window_view addSubview:titlebar_background_view_ + positioned:NSWindowBelow + relativeTo:nil]; + [titlebar_background_view_ setColor:gfx::SkColorToSRGBNSColor(active_frame_color_) inactiveColor:gfx::SkColorToSRGBNSColor(inactive_frame_color_)]; } @@ -820,6 +801,7 @@ void NativeAppWindowCocoa::WindowWillClose() { } void NativeAppWindowCocoa::WindowDidBecomeKey() { + [titlebar_background_view_ setNeedsDisplay:YES]; content::RenderWidgetHostView* rwhv = WebContents()->GetRenderWidgetHostView(); if (rwhv) @@ -837,6 +819,8 @@ void NativeAppWindowCocoa::WindowDidResignKey() { if ([NSApp isActive] && ([NSApp keyWindow] == window())) return; + [titlebar_background_view_ setNeedsDisplay:YES]; + WebContents()->StoreFocus(); content::RenderWidgetHostView* rwhv = -- 2.11.4.GIT