mac: Import chromium's CustomFrameView code.
authorCheng Zhao <zcbenz@gmail.com>
Wed, 11 Sep 2013 05:46:36 +0000 (13:46 +0800)
committerCheng Zhao <zcbenz@gmail.com>
Wed, 11 Sep 2013 05:46:36 +0000 (13:46 +0800)
atom.gyp
browser/native_window_mac.mm
browser/ui/cocoa/custom_frame_view.h [new file with mode: 0644]
browser/ui/cocoa/custom_frame_view.mm [new file with mode: 0644]

index c08e57d..9d0d20b 100644 (file)
--- a/atom.gyp
+++ b/atom.gyp
       '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',
index 4994fad..87fafd6 100644 (file)
@@ -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 (file)
index 0000000..7ba6601
--- /dev/null
@@ -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 <Cocoa/Cocoa.h>
+
+// 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 (file)
index 0000000..de89048
--- /dev/null
@@ -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 <objc/runtime.h>
+#import <Carbon/Carbon.h>
+
+#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 <http://crbug.com/114745>.
+  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