Browse Source

fix: fallback to FullSizeContentView for frameless window on mac (#13599)

fix: draggable regions not working on macOS
Cheng Zhao 6 years ago
parent
commit
0ec4ad288d

+ 5 - 0
atom/browser/api/atom_api_browser_window.cc

@@ -85,6 +85,11 @@ BrowserWindow::BrowserWindow(v8::Isolate* isolate,
 
   InitWith(isolate, wrapper);
 
+#if defined(OS_MACOSX)
+  if (!window()->has_frame())
+    OverrideNSWindowContentView();
+#endif
+
   // Init window after everything has been setup.
   window()->InitFromOptions(options);
 }

+ 4 - 0
atom/browser/api/atom_api_browser_window.h

@@ -80,6 +80,10 @@ class BrowserWindow : public TopLevelWindow,
   v8::Local<v8::Value> GetWebContents(v8::Isolate* isolate);
 
  private:
+#if defined(OS_MACOSX)
+  void OverrideNSWindowContentView();
+#endif
+
   // Helpers.
 
   // Called when the window needs to update its draggable region.

+ 12 - 0
atom/browser/api/atom_api_browser_window_mac.mm

@@ -7,6 +7,7 @@
 #import <Cocoa/Cocoa.h>
 
 #include "atom/browser/native_browser_view.h"
+#include "atom/browser/native_window_mac.h"
 #include "atom/common/draggable_region.h"
 #include "base/mac/scoped_nsobject.h"
 
@@ -53,6 +54,17 @@ std::vector<gfx::Rect> CalculateNonDraggableRegions(
 
 }  // namespace
 
+void BrowserWindow::OverrideNSWindowContentView() {
+  // Make NativeWindow use a NSView as content view.
+  static_cast<NativeWindowMac*>(window())->OverrideNSWindowContentView();
+  // Add webview to contentView.
+  NSView* webView = web_contents()->GetNativeView();
+  NSView* contentView = [window()->GetNativeWindow() contentView];
+  [webView setFrame:[contentView bounds]];
+  [contentView addSubview:webView];
+  [contentView viewDidMoveToWindow];
+}
+
 void BrowserWindow::UpdateDraggableRegions(
     content::RenderFrameHost* rfh,
     const std::vector<DraggableRegion>& regions) {

+ 6 - 0
atom/browser/native_window_mac.h

@@ -128,6 +128,9 @@ class NativeWindowMac : public NativeWindow {
   gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) const override;
   gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) const override;
 
+  // Use a custom content view instead of Chromium's BridgedContentView.
+  void OverrideNSWindowContentView();
+
   // Set the attribute of NSWindow while work around a bug of zoom button.
   void SetStyleMask(bool on, NSUInteger flag);
   void SetCollectionBehavior(bool on, NSUInteger flag);
@@ -152,6 +155,9 @@ class NativeWindowMac : public NativeWindow {
   views::View* GetContentsView() override;
 
  private:
+  // Add custom layers to the content view.
+  void AddContentViewLayers();
+
   void InternalSetParentWindow(NativeWindow* parent, bool attach);
   void ShowWindowButton(NSWindowButton button);
 

+ 96 - 55
atom/browser/native_window_mac.mm

@@ -32,6 +32,31 @@
 #include "ui/views/cocoa/bridged_native_widget.h"
 #include "ui/views/widget/widget.h"
 
+// This view always takes the size of its superview. It is intended to be used
+// as a NSWindow's contentView.  It is needed because NSWindow's implementation
+// explicitly resizes the contentView at inopportune times.
+@interface FullSizeContentView : NSView
+@end
+
+@implementation FullSizeContentView
+
+// This method is directly called by NSWindow during a window resize on OSX
+// 10.10.0, beta 2. We must override it to prevent the content view from
+// shrinking.
+- (void)setFrameSize:(NSSize)size {
+  if ([self superview])
+    size = [[self superview] bounds].size;
+  [super setFrameSize:size];
+}
+
+// The contentView gets moved around during certain full-screen operations.
+// This is less than ideal, and should eventually be removed.
+- (void)viewDidMoveToSuperview {
+  [self setFrame:[[self superview] bounds]];
+}
+
+@end
+
 // Custom Quit, Minimize and Full Screen button container for frameless
 // windows.
 @interface CustomWindowButtonView : NSView {
@@ -443,61 +468,7 @@ NativeWindowMac::NativeWindowMac(const mate::Dictionary& options,
 
   // Default content view.
   SetContentView(new views::View());
-
-  // Make sure the bottom corner is rounded for non-modal windows:
-  // http://crbug.com/396264. But do not enable it on OS X 10.9 for transparent
-  // window, otherwise a semi-transparent frame would show.
-  if (!(transparent() && base::mac::IsOS10_9()) && !is_modal()) {
-    base::scoped_nsobject<CALayer> background_layer([[CALayer alloc] init]);
-    [background_layer
-        setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable];
-    [[window_ contentView] setLayer:background_layer];
-    [[window_ contentView] setWantsLayer:YES];
-  }
-
-  if (!has_frame()) {
-    // In OSX 10.10, adding subviews to the root view for the NSView hierarchy
-    // produces warnings. To eliminate the warnings, we resize the contentView
-    // to fill the window, and add subviews to that.
-    // http://crbug.com/380412
-    if (!original_set_frame_size) {
-      Class cl = [[window_ contentView] class];
-      original_set_frame_size = class_replaceMethod(
-          cl, @selector(setFrameSize:), (IMP)SetFrameSize, "v@:{_NSSize=ff}");
-      original_view_did_move_to_superview =
-          class_replaceMethod(cl, @selector(viewDidMoveToSuperview),
-                              (IMP)ViewDidMoveToSuperview, "v@:");
-      [[window_ contentView] viewDidMoveToWindow];
-    }
-
-    // The fullscreen button should always be hidden for frameless window.
-    [[window_ standardWindowButton:NSWindowFullScreenButton] setHidden:YES];
-
-    if (title_bar_style_ == CUSTOM_BUTTONS_ON_HOVER) {
-      buttons_view_.reset(
-          [[CustomWindowButtonView alloc] initWithFrame:NSZeroRect]);
-      [[window_ contentView] addSubview:buttons_view_];
-    } else {
-      if (title_bar_style_ != NORMAL) {
-        if (base::mac::IsOS10_9()) {
-          ShowWindowButton(NSWindowZoomButton);
-          ShowWindowButton(NSWindowMiniaturizeButton);
-          ShowWindowButton(NSWindowCloseButton);
-        }
-        return;
-      }
-
-      // Hide the window buttons.
-      [[window_ standardWindowButton:NSWindowZoomButton] setHidden:YES];
-      [[window_ standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
-      [[window_ standardWindowButton:NSWindowCloseButton] setHidden:YES];
-    }
-
-    // Some third-party macOS utilities check the zoom button's enabled state to
-    // determine whether to show custom UI on hover, so we disable it here to
-    // prevent them from doing so in a frameless app window.
-    [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:NO];
-  }
+  AddContentViewLayers();
 }
 
 NativeWindowMac::~NativeWindowMac() {
@@ -1320,6 +1291,63 @@ views::View* NativeWindowMac::GetContentsView() {
   return root_view_.get();
 }
 
+void NativeWindowMac::AddContentViewLayers() {
+  // Make sure the bottom corner is rounded for non-modal windows:
+  // http://crbug.com/396264. But do not enable it on OS X 10.9 for transparent
+  // window, otherwise a semi-transparent frame would show.
+  if (!(transparent() && base::mac::IsOS10_9()) && !is_modal()) {
+    base::scoped_nsobject<CALayer> background_layer([[CALayer alloc] init]);
+    [background_layer
+        setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable];
+    [[window_ contentView] setLayer:background_layer];
+    [[window_ contentView] setWantsLayer:YES];
+  }
+
+  if (!has_frame()) {
+    // In OSX 10.10, adding subviews to the root view for the NSView hierarchy
+    // produces warnings. To eliminate the warnings, we resize the contentView
+    // to fill the window, and add subviews to that.
+    // http://crbug.com/380412
+    if (!original_set_frame_size) {
+      Class cl = [[window_ contentView] class];
+      original_set_frame_size = class_replaceMethod(
+          cl, @selector(setFrameSize:), (IMP)SetFrameSize, "v@:{_NSSize=ff}");
+      original_view_did_move_to_superview =
+          class_replaceMethod(cl, @selector(viewDidMoveToSuperview),
+                              (IMP)ViewDidMoveToSuperview, "v@:");
+      [[window_ contentView] viewDidMoveToWindow];
+    }
+
+    // The fullscreen button should always be hidden for frameless window.
+    [[window_ standardWindowButton:NSWindowFullScreenButton] setHidden:YES];
+
+    if (title_bar_style_ == CUSTOM_BUTTONS_ON_HOVER) {
+      buttons_view_.reset(
+          [[CustomWindowButtonView alloc] initWithFrame:NSZeroRect]);
+      [[window_ contentView] addSubview:buttons_view_];
+    } else {
+      if (title_bar_style_ != NORMAL) {
+        if (base::mac::IsOS10_9()) {
+          ShowWindowButton(NSWindowZoomButton);
+          ShowWindowButton(NSWindowMiniaturizeButton);
+          ShowWindowButton(NSWindowCloseButton);
+        }
+        return;
+      }
+
+      // Hide the window buttons.
+      [[window_ standardWindowButton:NSWindowZoomButton] setHidden:YES];
+      [[window_ standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
+      [[window_ standardWindowButton:NSWindowCloseButton] setHidden:YES];
+    }
+
+    // Some third-party macOS utilities check the zoom button's enabled state to
+    // determine whether to show custom UI on hover, so we disable it here to
+    // prevent them from doing so in a frameless app window.
+    [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:NO];
+  }
+}
+
 void NativeWindowMac::InternalSetParentWindow(NativeWindow* parent,
                                               bool attach) {
   if (is_modal())
@@ -1350,6 +1378,19 @@ void NativeWindowMac::SetForwardMouseMessages(bool forward) {
   [window_ setAcceptsMouseMovedEvents:forward];
 }
 
+void NativeWindowMac::OverrideNSWindowContentView() {
+  // When using `views::Widget` to hold WebContents, Chromium would use
+  // `BridgedContentView` as content view, which does not support draggable
+  // regions. In order to make draggable regions work, we have to replace the
+  // content view with a simple NSView.
+  container_view_.reset([[FullSizeContentView alloc] init]);
+  [container_view_
+      setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+  [container_view_ setFrame:[[[window_ contentView] superview] bounds]];
+  [window_ setContentView:container_view_];
+  AddContentViewLayers();
+}
+
 void NativeWindowMac::SetStyleMask(bool on, NSUInteger flag) {
   // Changing the styleMask of a frameless windows causes it to change size so
   // we explicitly disable resizing while setting it.