From f38eb1b66fccead9dc1367faf4279d1b3766f857 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 11 Sep 2013 13:46:36 +0800 Subject: [PATCH] mac: Import chromium's CustomFrameView code. --- atom.gyp | 2 + browser/native_window_mac.mm | 3 +- browser/ui/cocoa/custom_frame_view.h | 56 ++++++++++++++ browser/ui/cocoa/custom_frame_view.mm | 138 ++++++++++++++++++++++++++++++++++ 4 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 browser/ui/cocoa/custom_frame_view.h create mode 100644 browser/ui/cocoa/custom_frame_view.mm diff --git a/atom.gyp b/atom.gyp index c08e57d..9d0d20b 100644 --- a/atom.gyp +++ b/atom.gyp @@ -109,6 +109,8 @@ 'browser/ui/accelerator_util_win.cc', 'browser/ui/atom_menu_controller_mac.h', 'browser/ui/atom_menu_controller_mac.mm', + 'browser/ui/cocoa/custom_frame_view.h', + 'browser/ui/cocoa/custom_frame_view.mm', 'browser/ui/file_dialog.h', 'browser/ui/file_dialog_mac.mm', 'browser/ui/file_dialog_win.cc', diff --git a/browser/native_window_mac.mm b/browser/native_window_mac.mm index 4994fad..87fafd6 100644 --- a/browser/native_window_mac.mm +++ b/browser/native_window_mac.mm @@ -14,6 +14,7 @@ #include "base/strings/sys_string_conversions.h" #include "base/values.h" #import "browser/atom_event_processing_window.h" +#import "browser/ui/cocoa/custom_frame_view.h" #include "brightray/browser/inspectable_web_contents.h" #include "brightray/browser/inspectable_web_contents_view.h" #include "common/draggable_region.h" @@ -203,7 +204,7 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents, styleMask:style_mask backing:NSBackingStoreBuffered defer:YES] : - [[AtomNSWindow alloc] + [[AtomFramelessNSWindow alloc] initWithContentRect:cocoa_bounds styleMask:style_mask backing:NSBackingStoreBuffered diff --git a/browser/ui/cocoa/custom_frame_view.h b/browser/ui/cocoa/custom_frame_view.h new file mode 100644 index 0000000..7ba6601 --- /dev/null +++ b/browser/ui/cocoa/custom_frame_view.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +// CustomFrameView is a class whose methods we swizzle into NSGrayFrame +// on 10.7 and below, or NSThemeFrame on 10.8 and above, so that we can +// support custom frame drawing. This is used with a textured window so that +// AppKit does not draw a title bar. +// This class is never to be instantiated on its own. +// We explored a variety of ways to support custom frame drawing and custom +// window widgets. +// Our requirements were: +// a) that we could fall back on standard system drawing at any time for the +// "default theme" +// b) We needed to be able to draw both a background pattern, and an overlay +// graphic, and we need to be able to set the pattern phase of our background +// window. +// c) We had to be able to support "transparent" themes, so that you could see +// through to the underlying windows in places without the system theme +// getting in the way. +// +// Since we want "A" we couldn't just do a transparent borderless window. At +// least I couldn't find the right combination of HITheme calls to make it draw +// nicely, and I don't trust that the HITheme calls are going to exist in future +// system versions. +// "C" precluded us from inserting a view between the system frame and the +// the content frame in Z order. To get the transparency we actually need to +// replace the drawing of the system frame. +// "B" precluded us from just setting a background color on the window. +// +// Originally we tried overriding the private API +frameViewForStyleMask: to +// add our own subclass of NSGrayView to our window. Turns out that if you +// subclass NSGrayView it does not draw correctly when you call NSGrayView's +// drawRect. It appears that NSGrayView's drawRect: method (and that of its +// superclasses) do lots of "isMemberOfClass/isKindOfClass" calls, and if your +// class is NOT an instance of NSGrayView (as opposed to a subclass of +// NSGrayView) then the system drawing will not work correctly. +// +// Given all of the above, we found swizzling drawRect in _load to be the +// easiest and safest method of achieving our goals. We do the best we can to +// check that everything is safe, and attempt to fallback gracefully if it is +// not. + +@interface NSWindow (CustomFrameView) + +// To define custom window drawing behaviour, override this method on an +// NSWindow subclass. Call the default method (on super) to draw the +// default frame. +// NOTE: Always call the default implementation (even if you just immediately +// draw over it), otherwise the top-left and top-right corners of the window +// won't be drawn correctly. +- (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view; + +@end \ No newline at end of file diff --git a/browser/ui/cocoa/custom_frame_view.mm b/browser/ui/cocoa/custom_frame_view.mm new file mode 100644 index 0000000..de89048 --- /dev/null +++ b/browser/ui/cocoa/custom_frame_view.mm @@ -0,0 +1,138 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "browser/ui/cocoa/custom_frame_view.h" + +#import +#import + +#include "base/logging.h" +#include "base/mac/mac_util.h" +#include "base/mac/scoped_nsautorelease_pool.h" + +namespace { +BOOL gCanDrawTitle = NO; +BOOL gCanGetCornerRadius = NO; +} // namespace + +@interface NSView (Swizzles) +- (void)drawRectOriginal:(NSRect)rect; +- (NSPoint)_fullScreenButtonOriginOriginal; +@end + +@interface NSWindow (FramedBrowserWindow) +- (NSPoint)fullScreenButtonOriginAdjustment; +@end + +@implementation NSWindow (CustomFrameView) +- (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view { + [view drawRectOriginal:rect]; +} +@end + +@interface CustomFrameView : NSView + +@end + +@implementation CustomFrameView + +// This is where we swizzle drawRect, and add in two methods that we +// need. If any of these fail it shouldn't affect the functionality of the +// others. If they all fail, we will lose window frame theming and +// roll overs for our close widgets, but things should still function +// correctly. ++ (void)load { + base::mac::ScopedNSAutoreleasePool pool; + + // On 10.8+ the background for textured windows are no longer drawn by + // NSGrayFrame, and NSThemeFrame is used instead . + Class borderViewClass = NSClassFromString( + base::mac::IsOSMountainLionOrLater() ? @"NSThemeFrame" : @"NSGrayFrame"); + DCHECK(borderViewClass); + if (!borderViewClass) return; + + // Exchange draw rect. + Method m0 = class_getInstanceMethod([self class], @selector(drawRect:)); + DCHECK(m0); + if (m0) { + BOOL didAdd = class_addMethod(borderViewClass, + @selector(drawRectOriginal:), + method_getImplementation(m0), + method_getTypeEncoding(m0)); + DCHECK(didAdd); + if (didAdd) { + Method m1 = class_getInstanceMethod(borderViewClass, + @selector(drawRect:)); + Method m2 = class_getInstanceMethod(borderViewClass, + @selector(drawRectOriginal:)); + DCHECK(m1 && m2); + if (m1 && m2) { + method_exchangeImplementations(m1, m2); + } + } + } + + // Swizzle the method that sets the origin for the Lion fullscreen button. Do + // nothing if it cannot be found. + m0 = class_getInstanceMethod([self class], + @selector(_fullScreenButtonOrigin)); + if (m0) { + BOOL didAdd = class_addMethod(borderViewClass, + @selector(_fullScreenButtonOriginOriginal), + method_getImplementation(m0), + method_getTypeEncoding(m0)); + if (didAdd) { + Method m1 = class_getInstanceMethod(borderViewClass, + @selector(_fullScreenButtonOrigin)); + Method m2 = class_getInstanceMethod(borderViewClass, + @selector(_fullScreenButtonOriginOriginal)); + if (m1 && m2) { + method_exchangeImplementations(m1, m2); + } + } + } +} + ++ (BOOL)canDrawTitle { + return gCanDrawTitle; +} + ++ (BOOL)canGetCornerRadius { + return gCanGetCornerRadius; +} + +- (id)initWithFrame:(NSRect)frame { + // This class is not for instantiating. + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (id)initWithCoder:(NSCoder*)coder { + // This class is not for instantiating. + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +// Here is our custom drawing for our frame. +- (void)drawRect:(NSRect)rect { + // Delegate drawing to the window, whose default implementation (above) is to + // call into the original implementation. + [[self window] drawCustomFrameRect:rect forView:self]; +} + +// Override to move the fullscreen button to the left of the profile avatar. +- (NSPoint)_fullScreenButtonOrigin { + NSWindow* window = [self window]; + NSPoint offset = NSZeroPoint; + + if ([window respondsToSelector:@selector(fullScreenButtonOriginAdjustment)]) + offset = [window fullScreenButtonOriginAdjustment]; + + NSPoint origin = [self _fullScreenButtonOriginOriginal]; + origin.x += offset.x; + origin.y += offset.y; + return origin; +} + +@end -- 2.7.4