Merge pull request #12787 from electron/mac-content-view

Refactor NativeWindow (Part 12): Do not use custom content view on macOS
This commit is contained in:
Cheng Zhao 2018-05-04 12:48:15 +09:00 committed by GitHub
commit 7be30bb249
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 215 additions and 82 deletions

View file

@ -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<AtomNSWindowDelegate> window_delegate_;
base::scoped_nsobject<AtomPreviewItem> preview_item_;
base::scoped_nsobject<AtomTouchBar> touch_bar_;
base::scoped_nsobject<CustomWindowButtonView> 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<FullSizeContentView> container_view_;
// The content view passed by SetContentView, weak ref.
NSView* content_view_;
// The view that fills the client area.
std::unique_ptr<RootViewMac> root_view_;
// The content view, managed by widget_.
views::NativeViewHost* content_view_;
bool is_kiosk_;
bool was_fullscreen_;

View file

@ -5,6 +5,7 @@
#include "atom/browser/native_window_mac.h"
#include <AvailabilityMacros.h>
#include <objc/objc-runtime.h>
#include <string>
@ -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<atom::NativeWindowMac::TitleBarStyle> {
namespace atom {
namespace {
bool IsFramelessWindow(NSView* view) {
NativeWindow* window = [static_cast<AtomNSWindow*>([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<decltype(&SetFrameSize)>(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<decltype(&SetFrameSize)>(
[[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<decltype(&ViewDidMoveToSuperview)>(
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<AtomNSWindow*>(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<ui::AcceleratedWidgetMacNSView*>(bridge)->
AcceleratedWidgetGetNSView();
static_cast<ui::AcceleratedWidgetMacNSView*>(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())

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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())

View file

@ -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_

View file

@ -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<NativeWindowMac*>(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

View file

@ -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',