diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index 53f774dacf51..0fb2359e0e63 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -12,15 +12,23 @@ #include "atom/browser/native_window.h" #include "base/mac/scoped_nsobject.h" +#include "ui/views/controls/native/native_view_host.h" @class AtomNSWindow; @class AtomNSWindowDelegate; @class AtomPreviewItem; @class AtomTouchBar; +@class CustomWindowButtonView; @class FullSizeContentView; +namespace views { +class NativeViewHost; +} + namespace atom { +class RootViewMac; + class NativeWindowMac : public NativeWindow { public: NativeWindowMac(const mate::Dictionary& options, NativeWindow* parent); @@ -136,6 +144,7 @@ class NativeWindowMac : public NativeWindow { }; TitleBarStyle title_bar_style() const { return title_bar_style_; } + views::View* content_view() { return content_view_; } AtomPreviewItem* preview_item() const { return preview_item_.get(); } AtomTouchBar* touch_bar() const { return touch_bar_.get(); } bool zoom_to_page_width() const { return zoom_to_page_width_; } @@ -145,6 +154,7 @@ class NativeWindowMac : public NativeWindow { protected: // views::WidgetDelegate: bool CanResize() const override; + views::View* GetContentsView() override; private: void InternalSetParentWindow(NativeWindow* parent, bool attach); @@ -157,6 +167,7 @@ class NativeWindowMac : public NativeWindow { base::scoped_nsobject window_delegate_; base::scoped_nsobject preview_item_; base::scoped_nsobject touch_bar_; + base::scoped_nsobject buttons_view_; // Event monitor for scroll wheel event. id wheel_event_monitor_; @@ -164,8 +175,11 @@ class NativeWindowMac : public NativeWindow { // The view that will fill the whole frameless window. base::scoped_nsobject container_view_; - // The content view passed by SetContentView, weak ref. - NSView* content_view_; + // The view that fills the client area. + std::unique_ptr root_view_; + + // The content view, managed by widget_. + views::NativeViewHost* content_view_; bool is_kiosk_; bool was_fullscreen_; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index feb2f8c6f88d..0ff821ac8700 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -5,6 +5,7 @@ #include "atom/browser/native_window_mac.h" #include +#include #include @@ -14,6 +15,7 @@ #include "atom/browser/ui/cocoa/atom_ns_window_delegate.h" #include "atom/browser/ui/cocoa/atom_preview_item.h" #include "atom/browser/ui/cocoa/atom_touch_bar.h" +#include "atom/browser/ui/cocoa/root_view_mac.h" #include "atom/browser/window_list.h" #include "atom/common/options_switches.h" #include "base/mac/mac_util.h" @@ -126,31 +128,6 @@ @end -// 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 - #if !defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER) enum { NSWindowTabbingModeDisallowed = 2 }; @@ -233,10 +210,54 @@ struct Converter { namespace atom { +namespace { + +bool IsFramelessWindow(NSView* view) { + NativeWindow* window = [static_cast([view window]) shell]; + return window && !window->has_frame(); +} + +IMP original_set_frame_size = nullptr; +IMP original_view_did_move_to_superview = nullptr; + +// 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(NSView* self, SEL _cmd, NSSize size) { + if (!IsFramelessWindow(self)) { + auto original = + reinterpret_cast(original_set_frame_size); + return original(self, _cmd, size); + } + // For frameless window, resize the view to cover full window. + if ([self superview]) + size = [[self superview] bounds].size; + auto super_impl = reinterpret_cast( + [[self superclass] instanceMethodForSelector:_cmd]); + super_impl(self, _cmd, size); +} + +// The contentView gets moved around during certain full-screen operations. +// This is less than ideal, and should eventually be removed. +void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { + if (!IsFramelessWindow(self)) { + // [BridgedContentView viewDidMoveToSuperview]; + auto original = reinterpret_cast( + original_view_did_move_to_superview); + if (original) + original(self, _cmd); + return; + } + [self setFrame:[[self superview] bounds]]; +} + +} // namespace + NativeWindowMac::NativeWindowMac(const mate::Dictionary& options, NativeWindow* parent) : NativeWindow(options, parent), - content_view_(nil), + root_view_(new RootViewMac(this)), + content_view_(nullptr), is_kiosk_(false), was_fullscreen_(false), zoom_to_page_width_(false), @@ -252,8 +273,7 @@ NativeWindowMac::NativeWindowMac(const mate::Dictionary& options, NSRect main_screen_rect = [[[NSScreen screens] firstObject] frame]; gfx::Rect bounds(round((NSWidth(main_screen_rect) - width) / 2), - round((NSHeight(main_screen_rect) - height) / 2), - width, + round((NSHeight(main_screen_rect) - height) / 2), width, height); options.Get(options::kResizable, &resizable_); @@ -313,11 +333,10 @@ NativeWindowMac::NativeWindowMac(const mate::Dictionary& options, params.bounds = bounds; params.delegate = this; params.type = views::Widget::InitParams::TYPE_WINDOW; - params.native_widget = new AtomNativeWidgetMac(styleMask, widget()); + params.native_widget = new AtomNativeWidgetMac(this, styleMask, widget()); widget()->Init(params); window_ = static_cast(widget()->GetNativeWindow()); - [window_ setShell:this]; [window_ setEnableLargerThanScreen:enable_larger_than_screen()]; window_delegate_.reset([[AtomNSWindowDelegate alloc] initWithShell:this]); @@ -432,19 +451,6 @@ NativeWindowMac::NativeWindowMac(const mate::Dictionary& options, // Set maximizable state last to ensure zoom button does not get reset // by calls to other APIs. SetMaximizable(maximizable); -} - -NativeWindowMac::~NativeWindowMac() { - [NSEvent removeMonitor:wheel_event_monitor_]; -} - -void NativeWindowMac::SetContentView( - brightray::InspectableWebContents* web_contents) { - if (content_view_) - [content_view_ removeFromSuperview]; - - content_view_ = web_contents->GetView()->GetNativeView(); - [content_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; // 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 @@ -457,39 +463,28 @@ void NativeWindowMac::SetContentView( [[window_ contentView] setWantsLayer:YES]; } - if (has_frame()) { - [content_view_ setFrame:[[window_ contentView] bounds]]; - [[window_ contentView] addSubview:content_view_]; - } else { + 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 - container_view_.reset([[FullSizeContentView alloc] init]); - [container_view_ - setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [container_view_ setFrame:[[[window_ contentView] superview] bounds]]; - - // Move the vibrantView from the old content view. - if ([window_ vibrantView]) { - [[window_ vibrantView] removeFromSuperview]; - [container_view_ addSubview:[window_ vibrantView] - positioned:NSWindowBelow - relativeTo:nil]; + 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]; } - [window_ setContentView:container_view_]; - - [content_view_ setFrame:[container_view_ bounds]]; - [container_view_ addSubview:content_view_]; - // The fullscreen button should always be hidden for frameless window. [[window_ standardWindowButton:NSWindowFullScreenButton] setHidden:YES]; if (title_bar_style_ == CUSTOM_BUTTONS_ON_HOVER) { - NSView* window_button_view = [[[CustomWindowButtonView alloc] - initWithFrame:NSZeroRect] autorelease]; - [container_view_ addSubview:window_button_view]; + buttons_view_.reset( + [[CustomWindowButtonView alloc] initWithFrame:NSZeroRect]); + [[window_ contentView] addSubview:buttons_view_]; } else { if (title_bar_style_ != NORMAL) { if (base::mac::IsOS10_9()) { @@ -513,6 +508,29 @@ void NativeWindowMac::SetContentView( } } +NativeWindowMac::~NativeWindowMac() { + [NSEvent removeMonitor:wheel_event_monitor_]; +} + +void NativeWindowMac::SetContentView( + brightray::InspectableWebContents* web_contents) { + views::View* root_view = GetContentsView(); + if (content_view_) + root_view->RemoveChildView(content_view_); + + content_view_ = new views::NativeViewHost(); + root_view->AddChildView(content_view_); + content_view_->Attach(web_contents->GetView()->GetNativeView()); + + if (buttons_view_) { + // Ensure the buttons view are always floated on the top. + [buttons_view_ removeFromSuperview]; + [[window_ contentView] addSubview:buttons_view_]; + } + + root_view->Layout(); +} + void NativeWindowMac::Close() { // When this is a sheet showing, performClose won't work. if (is_modal() && parent() && IsVisible()) { @@ -696,7 +714,7 @@ void NativeWindowMac::SetContentSizeConstraints( // will result in actual content size being larger. if (!has_frame()) { NSRect frame = NSMakeRect(0, 0, size.width(), size.height()); - NSRect content = [window_ contentRectForFrameRect:frame]; + NSRect content = [window_ originalContentRectForFrameRect:frame]; return content.size; } else { return NSMakeSize(size.width(), size.height()); @@ -984,8 +1002,8 @@ void NativeWindowMac::SetBackgroundColor(SkColor color) { // views::Widget adds a layer for the content view. auto* bridge = views::NativeWidgetMac::GetBridgeForNativeWindow(window_); NSView* compositor_superview = - static_cast(bridge)-> - AcceleratedWidgetGetNSView(); + static_cast(bridge) + ->AcceleratedWidgetGetNSView(); [[compositor_superview layer] setBackgroundColor:cgcolor]; // When using WebContents as content view, the contentView also has layer. if ([[window_ contentView] wantsLayer]) @@ -1308,6 +1326,10 @@ bool NativeWindowMac::CanResize() const { return resizable_; } +views::View* NativeWindowMac::GetContentsView() { + return root_view_.get(); +} + void NativeWindowMac::InternalSetParentWindow(NativeWindow* parent, bool attach) { if (is_modal()) diff --git a/atom/browser/ui/cocoa/atom_native_widget_mac.h b/atom/browser/ui/cocoa/atom_native_widget_mac.h index e5f711e80b65..c41d102fcd74 100644 --- a/atom/browser/ui/cocoa/atom_native_widget_mac.h +++ b/atom/browser/ui/cocoa/atom_native_widget_mac.h @@ -9,9 +9,12 @@ namespace atom { +class NativeWindowMac; + class AtomNativeWidgetMac : public views::NativeWidgetMac { public: - AtomNativeWidgetMac(NSUInteger style_mask, + AtomNativeWidgetMac(NativeWindowMac* shell, + NSUInteger style_mask, views::internal::NativeWidgetDelegate* delegate); ~AtomNativeWidgetMac() override; @@ -21,6 +24,7 @@ class AtomNativeWidgetMac : public views::NativeWidgetMac { const views::Widget::InitParams& params) override; private: + NativeWindowMac* shell_; NSUInteger style_mask_; DISALLOW_COPY_AND_ASSIGN(AtomNativeWidgetMac); diff --git a/atom/browser/ui/cocoa/atom_native_widget_mac.mm b/atom/browser/ui/cocoa/atom_native_widget_mac.mm index 9cb5da48bc11..0eccfb093e4c 100644 --- a/atom/browser/ui/cocoa/atom_native_widget_mac.mm +++ b/atom/browser/ui/cocoa/atom_native_widget_mac.mm @@ -5,25 +5,23 @@ #include "atom/browser/ui/cocoa/atom_native_widget_mac.h" #include "atom/browser/ui/cocoa/atom_ns_window.h" -#include "ui/base/cocoa/window_size_constants.h" namespace atom { AtomNativeWidgetMac::AtomNativeWidgetMac( + NativeWindowMac* shell, NSUInteger style_mask, views::internal::NativeWidgetDelegate* delegate) : views::NativeWidgetMac(delegate), + shell_(shell), style_mask_(style_mask) {} AtomNativeWidgetMac::~AtomNativeWidgetMac() {} NativeWidgetMacNSWindow* AtomNativeWidgetMac::CreateNSWindow( const views::Widget::InitParams& params) { - return [[[AtomNSWindow alloc] - initWithContentRect:ui::kWindowSizeDeterminedLater - styleMask:style_mask_ - backing:NSBackingStoreBuffered - defer:YES] autorelease]; + return [[[AtomNSWindow alloc] initWithShell:shell_ styleMask:style_mask_] + autorelease]; } } // namespace atom diff --git a/atom/browser/ui/cocoa/atom_ns_window.h b/atom/browser/ui/cocoa/atom_ns_window.h index 885e19c6dcc4..7fd8f2a9a2a2 100644 --- a/atom/browser/ui/cocoa/atom_ns_window.h +++ b/atom/browser/ui/cocoa/atom_ns_window.h @@ -36,8 +36,11 @@ class ScopedDisableResize { @property BOOL disableAutoHideCursor; @property BOOL disableKeyOrMainWindow; @property NSPoint windowButtonsOffset; -@property (nonatomic, retain) NSView* vibrantView; -- (void)setShell:(atom::NativeWindowMac*)shell; +@property(nonatomic, retain) NSView* vibrantView; +- (id)initWithShell:(atom::NativeWindowMac*)shell + styleMask:(NSUInteger)styleMask; +- (atom::NativeWindowMac*)shell; +- (NSRect)originalContentRectForFrameRect:(NSRect)frameRect; - (void)enableWindowButtonsOffset; - (void)toggleFullScreenMode:(id)sender; @end diff --git a/atom/browser/ui/cocoa/atom_ns_window.mm b/atom/browser/ui/cocoa/atom_ns_window.mm index 386156f988a0..56ed5dc2319c 100644 --- a/atom/browser/ui/cocoa/atom_ns_window.mm +++ b/atom/browser/ui/cocoa/atom_ns_window.mm @@ -7,6 +7,7 @@ #include "atom/browser/native_window_mac.h" #include "atom/browser/ui/cocoa/atom_preview_item.h" #include "atom/browser/ui/cocoa/atom_touch_bar.h" +#include "ui/base/cocoa/window_size_constants.h" namespace atom { @@ -23,8 +24,23 @@ bool ScopedDisableResize::disable_resize_ = false; @synthesize windowButtonsOffset; @synthesize vibrantView; -- (void)setShell:(atom::NativeWindowMac*)shell { - shell_ = shell; +- (id)initWithShell:(atom::NativeWindowMac*)shell + styleMask:(NSUInteger)styleMask { + if ((self = [super initWithContentRect:ui::kWindowSizeDeterminedLater + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:YES])) { + shell_ = shell; + } + return self; +} + +- (atom::NativeWindowMac*)shell { + return shell_; +} + +- (NSRect)originalContentRectForFrameRect:(NSRect)frameRect { + return [super contentRectForFrameRect:frameRect]; } - (NSTouchBar*)makeTouchBar API_AVAILABLE(macosx(10.12.2)) { @@ -48,6 +64,13 @@ bool ScopedDisableResize::disable_resize_ = false; } } +- (NSRect)contentRectForFrameRect:(NSRect)frameRect { + if (shell_->has_frame()) + return [super contentRectForFrameRect:frameRect]; + else + return frameRect; +} + - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen*)screen { // Resizing is disabled. if (atom::ScopedDisableResize::IsResizeDisabled()) diff --git a/atom/browser/ui/cocoa/root_view_mac.h b/atom/browser/ui/cocoa/root_view_mac.h new file mode 100644 index 000000000000..07dc6e9e4c7f --- /dev/null +++ b/atom/browser/ui/cocoa/root_view_mac.h @@ -0,0 +1,33 @@ +// Copyright (c) 2018 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_UI_COCOA_ROOT_VIEW_MAC_H_ +#define ATOM_BROWSER_UI_COCOA_ROOT_VIEW_MAC_H_ + +#include "ui/views/view.h" + +namespace atom { + +class NativeWindow; + +class RootViewMac : public views::View { + public: + explicit RootViewMac(NativeWindow* window); + ~RootViewMac() override; + + // views::View: + void Layout() override; + gfx::Size GetMinimumSize() const override; + gfx::Size GetMaximumSize() const override; + + private: + // Parent window, weak ref. + NativeWindow* window_; + + DISALLOW_COPY_AND_ASSIGN(RootViewMac); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_UI_COCOA_ROOT_VIEW_MAC_H_ diff --git a/atom/browser/ui/cocoa/root_view_mac.mm b/atom/browser/ui/cocoa/root_view_mac.mm new file mode 100644 index 000000000000..6f9e81e879df --- /dev/null +++ b/atom/browser/ui/cocoa/root_view_mac.mm @@ -0,0 +1,34 @@ +// Copyright (c) 2018 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/ui/cocoa/root_view_mac.h" + +#include "atom/browser/native_window_mac.h" + +namespace atom { + +RootViewMac::RootViewMac(NativeWindow* window) : window_(window) { + set_owned_by_client(); +} + +RootViewMac::~RootViewMac() {} + +void RootViewMac::Layout() { + views::View* content_view = + static_cast(window_)->content_view(); + if (!content_view) // Not ready yet. + return; + + content_view->SetBoundsRect(gfx::Rect(gfx::Point(), size())); +} + +gfx::Size RootViewMac::GetMinimumSize() const { + return window_->GetMinimumSize(); +} + +gfx::Size RootViewMac::GetMaximumSize() const { + return window_->GetMaximumSize(); +} + +} // namespace atom diff --git a/filenames.gypi b/filenames.gypi index 44dcc015d58a..0ba5a9d73b6a 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -330,6 +330,8 @@ 'atom/browser/ui/cocoa/atom_touch_bar.mm', 'atom/browser/ui/cocoa/views_delegate_mac.h', 'atom/browser/ui/cocoa/views_delegate_mac.mm', + 'atom/browser/ui/cocoa/root_view_mac.mm', + 'atom/browser/ui/cocoa/root_view_mac.h', 'atom/browser/ui/cocoa/touch_bar_forward_declarations.h', 'atom/browser/ui/drag_util_mac.mm', 'atom/browser/ui/drag_util_views.cc',