electron/atom/browser/native_window_mac.mm

1386 lines
44 KiB
Text
Raw Normal View History

// Copyright (c) 2013 GitHub, Inc.
2014-04-25 09:49:37 +00:00
// Use of this source code is governed by the MIT license that can be
2013-04-12 07:04:46 +00:00
// found in the LICENSE file.
2014-03-16 00:30:26 +00:00
#include "atom/browser/native_window_mac.h"
2013-04-12 07:04:46 +00:00
2017-10-05 22:57:27 +00:00
#include <AvailabilityMacros.h>
2018-05-02 11:43:45 +00:00
#include <objc/objc-runtime.h>
2016-10-26 00:55:34 +00:00
#include <string>
Implement initial, experimental BrowserView API Right now, `<webview>` is the only way to embed additional content in a `BrowserWindow`. Unfortunately `<webview>` suffers from a [number of problems](https://github.com/electron/electron/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3Awebview%20). To make matters worse, many of these are upstream Chromium bugs instead of Electron-specific bugs. For us at [Figma](https://www.figma.com), the main issue is very slow performance. Despite the upstream improvements to `<webview>` through the OOPIF work, it is probable that there will continue to be `<webview>`-specific bugs in the future. Therefore, this introduces a `<webview>` alternative to called `BrowserView`, which... - is a thin wrapper around `api::WebContents` (so bugs in `BrowserView` will likely also be bugs in `BrowserWindow` web contents) - is instantiated in the main process like `BrowserWindow` (and unlike `<webview>`, which lives in the DOM of a `BrowserWindow` web contents) - needs to be added to a `BrowserWindow` to display something on the screen This implements the most basic API. The API is expected to evolve and change in the near future and has consequently been marked as experimental. Please do not use this API in production unless you are prepared to deal with breaking changes. In the future, we will want to change the API to support multiple `BrowserView`s per window. We will also want to consider z-ordering auto-resizing, and possibly even nested views.
2017-04-11 17:47:30 +00:00
#include "atom/browser/native_browser_view_mac.h"
2018-04-24 08:23:08 +00:00
#include "atom/browser/ui/cocoa/atom_native_widget_mac.h"
2018-04-19 07:53:12 +00:00
#include "atom/browser/ui/cocoa/atom_ns_window.h"
#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"
2014-03-16 00:30:26 +00:00
#include "atom/common/options_switches.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/strings/sys_string_conversions.h"
#include "brightray/browser/inspectable_web_contents.h"
#include "brightray/browser/inspectable_web_contents_view.h"
2015-04-13 12:10:51 +00:00
#include "content/public/browser/browser_accessibility_state.h"
#include "native_mate/dictionary.h"
#include "skia/ext/skia_utils_mac.h"
#include "ui/gfx/skia_util.h"
#include "ui/gl/gpu_switching_manager.h"
2018-04-24 10:00:44 +00:00
#include "ui/views/background.h"
2018-04-24 11:08:14 +00:00
#include "ui/views/cocoa/bridged_native_widget.h"
2018-04-19 06:26:34 +00:00
#include "ui/views/widget/widget.h"
2013-04-12 07:04:46 +00:00
// Custom Quit, Minimize and Full Screen button container for frameless
// windows.
@interface CustomWindowButtonView : NSView {
@private
BOOL mouse_inside_;
}
@end
@implementation CustomWindowButtonView
- (id)initWithFrame:(NSRect)frame {
2017-06-05 18:08:05 +00:00
self = [super initWithFrame:frame];
NSButton* close_button = [NSWindow standardWindowButton:NSWindowCloseButton
forStyleMask:NSTitledWindowMask];
NSButton* miniaturize_button =
[NSWindow standardWindowButton:NSWindowMiniaturizeButton
forStyleMask:NSTitledWindowMask];
NSButton* zoom_button = [NSWindow standardWindowButton:NSWindowZoomButton
2017-06-06 22:19:53 +00:00
forStyleMask:NSTitledWindowMask];
CGFloat x = 0;
const CGFloat space_between = 20;
[close_button setFrameOrigin:NSMakePoint(x, 0)];
x += space_between;
[self addSubview:close_button];
[miniaturize_button setFrameOrigin:NSMakePoint(x, 0)];
x += space_between;
[self addSubview:miniaturize_button];
[zoom_button setFrameOrigin:NSMakePoint(x, 0)];
x += space_between;
[self addSubview:zoom_button];
const auto last_button_frame = zoom_button.frame;
[self setFrameSize:NSMakeSize(last_button_frame.origin.x +
last_button_frame.size.width,
last_button_frame.size.height)];
mouse_inside_ = NO;
2017-06-06 22:19:53 +00:00
[self setNeedsDisplayForButtons];
2017-06-05 18:08:05 +00:00
return self;
}
- (void)viewDidMoveToWindow {
if (!self.window) {
return;
}
// Stay in upper left corner.
const CGFloat top_margin = 3;
const CGFloat left_margin = 7;
[self setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin];
[self setFrameOrigin:NSMakePoint(left_margin, self.window.frame.size.height -
self.frame.size.height -
top_margin)];
}
2017-06-05 18:08:05 +00:00
- (BOOL)_mouseInGroup:(NSButton*)button {
return mouse_inside_;
}
- (void)updateTrackingAreas {
auto tracking_area = [[[NSTrackingArea alloc]
initWithRect:NSZeroRect
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways |
NSTrackingInVisibleRect
owner:self
userInfo:nil] autorelease];
[self addTrackingArea:tracking_area];
}
2017-06-05 18:08:05 +00:00
- (void)mouseEntered:(NSEvent*)event {
[super mouseEntered:event];
mouse_inside_ = YES;
2017-06-05 18:08:05 +00:00
[self setNeedsDisplayForButtons];
}
2017-06-05 18:08:05 +00:00
- (void)mouseExited:(NSEvent*)event {
[super mouseExited:event];
mouse_inside_ = NO;
2017-06-05 18:08:05 +00:00
[self setNeedsDisplayForButtons];
}
- (void)setNeedsDisplayForButtons {
2017-06-05 18:08:05 +00:00
for (NSView* subview in self.subviews) {
[subview setHidden:!mouse_inside_];
2017-06-05 18:08:05 +00:00
[subview setNeedsDisplay:YES];
}
}
@end
2017-10-05 22:57:27 +00:00
#if !defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER)
2018-04-20 18:47:04 +00:00
enum { NSWindowTabbingModeDisallowed = 2 };
@interface NSWindow (SierraSDK)
- (void)setTabbingMode:(NSInteger)mode;
- (void)setTabbingIdentifier:(NSString*)identifier;
- (void)addTabbedWindow:(NSWindow*)window ordered:(NSWindowOrderingMode)ordered;
- (IBAction)selectPreviousTab:(id)sender;
- (IBAction)selectNextTab:(id)sender;
- (IBAction)mergeAllWindows:(id)sender;
- (IBAction)moveTabToNewWindow:(id)sender;
- (IBAction)toggleTabBar:(id)sender;
@end
2017-10-05 22:57:27 +00:00
#endif
2014-09-17 07:58:08 +00:00
@interface AtomProgressBar : NSProgressIndicator
@end
@implementation AtomProgressBar
- (void)drawRect:(NSRect)dirtyRect {
if (self.style != NSProgressIndicatorBarStyle)
return;
// Draw edges of rounded rect.
NSRect rect = NSInsetRect([self bounds], 1.0, 1.0);
2014-09-18 08:17:49 +00:00
CGFloat radius = rect.size.height / 2;
2018-04-20 18:47:04 +00:00
NSBezierPath* bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect
xRadius:radius
yRadius:radius];
2014-09-18 08:17:49 +00:00
[bezier_path setLineWidth:2.0];
[[NSColor grayColor] set];
[bezier_path stroke];
2014-09-17 07:58:08 +00:00
// Fill the rounded rect.
2014-09-18 08:17:49 +00:00
rect = NSInsetRect(rect, 2.0, 2.0);
radius = rect.size.height / 2;
2018-04-20 18:47:04 +00:00
bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect
xRadius:radius
yRadius:radius];
2014-09-18 08:17:49 +00:00
[bezier_path setLineWidth:1.0];
[bezier_path addClip];
2014-09-17 07:58:08 +00:00
// Calculate the progress width.
2018-04-20 18:47:04 +00:00
rect.size.width =
floor(rect.size.width * ([self doubleValue] / [self maxValue]));
2014-09-17 07:58:08 +00:00
// Fill the progress bar with color blue.
[[NSColor colorWithSRGBRed:0.2 green:0.6 blue:1 alpha:1] set];
NSRectFill(rect);
}
@end
namespace mate {
2018-04-20 18:47:04 +00:00
template <>
struct Converter<atom::NativeWindowMac::TitleBarStyle> {
2018-04-20 18:47:04 +00:00
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
atom::NativeWindowMac::TitleBarStyle* out) {
std::string title_bar_style;
if (!ConvertFromV8(isolate, val, &title_bar_style))
return false;
if (title_bar_style == "hidden") {
*out = atom::NativeWindowMac::HIDDEN;
} else if (title_bar_style == "hiddenInset") {
*out = atom::NativeWindowMac::HIDDEN_INSET;
} else if (title_bar_style == "customButtonsOnHover") {
*out = atom::NativeWindowMac::CUSTOM_BUTTONS_ON_HOVER;
} else {
return false;
}
return true;
}
};
} // namespace mate
2013-04-12 07:04:46 +00:00
namespace atom {
2018-05-02 11:43:45 +00:00
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;
2018-05-02 11:43:45 +00:00
}
[self setFrame:[[self superview] bounds]];
}
} // namespace
2018-04-08 11:20:43 +00:00
NativeWindowMac::NativeWindowMac(const mate::Dictionary& options,
NativeWindow* parent)
: NativeWindow(options, parent), root_view_(new RootViewMac(this)) {
int width = 800, height = 600;
options.Get(options::kWidth, &width);
options.Get(options::kHeight, &height);
2013-04-12 07:04:46 +00:00
NSRect main_screen_rect = [[[NSScreen screens] firstObject] frame];
2018-04-24 08:23:08 +00:00
gfx::Rect bounds(round((NSWidth(main_screen_rect) - width) / 2),
round((NSHeight(main_screen_rect) - height) / 2), width,
2018-04-24 08:23:08 +00:00
height);
2018-04-24 10:00:44 +00:00
options.Get(options::kResizable, &resizable_);
options.Get(options::kTitleBarStyle, &title_bar_style_);
options.Get(options::kZoomToPageWidth, &zoom_to_page_width_);
options.Get(options::kFullscreenWindowTitle, &fullscreen_window_title_);
options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_);
bool minimizable = true;
options.Get(options::kMinimizable, &minimizable);
2016-01-23 07:51:52 +00:00
2016-01-22 21:24:33 +00:00
bool maximizable = true;
options.Get(options::kMaximizable, &maximizable);
bool closable = true;
options.Get(options::kClosable, &closable);
2017-03-30 20:47:11 +00:00
std::string tabbingIdentifier;
options.Get(options::kTabbingIdentifier, &tabbingIdentifier);
2017-03-27 14:15:17 +00:00
std::string windowType;
options.Get(options::kType, &windowType);
bool useStandardWindow = true;
// eventually deprecate separate "standardWindow" option in favor of
// standard / textured window types
options.Get(options::kStandardWindow, &useStandardWindow);
if (windowType == "textured") {
useStandardWindow = false;
}
2017-06-05 19:50:18 +00:00
NSUInteger styleMask = NSTitledWindowMask;
if (@available(macOS 10.10, *)) {
if (title_bar_style_ == CUSTOM_BUTTONS_ON_HOVER &&
(!useStandardWindow || transparent() || !has_frame())) {
styleMask = NSFullSizeContentViewWindowMask;
}
2017-06-05 19:50:18 +00:00
}
if (minimizable) {
styleMask |= NSMiniaturizableWindowMask;
}
2016-01-23 10:23:18 +00:00
if (closable) {
styleMask |= NSClosableWindowMask;
}
if (title_bar_style_ != NORMAL) {
// The window without titlebar is treated the same with frameless window.
set_has_frame(false);
}
if (!useStandardWindow || transparent() || !has_frame()) {
styleMask |= NSTexturedBackgroundWindowMask;
}
2018-04-24 08:23:08 +00:00
// Create views::Widget and assign window_ with it.
// TODO(zcbenz): Get rid of the window_ in future.
views::Widget::InitParams params;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.bounds = bounds;
params.delegate = this;
params.type = views::Widget::InitParams::TYPE_WINDOW;
params.native_widget = new AtomNativeWidgetMac(this, styleMask, widget());
widget()->Init(params);
window_ = static_cast<AtomNSWindow*>(widget()->GetNativeWindow());
2018-04-24 08:23:08 +00:00
[window_ setEnableLargerThanScreen:enable_larger_than_screen()];
window_delegate_.reset([[AtomNSWindowDelegate alloc] initWithShell:this]);
[window_ setDelegate:window_delegate_];
// Only use native parent window for non-modal windows.
if (parent && !is_modal()) {
SetParentWindow(parent);
2016-06-19 03:06:08 +00:00
}
if (transparent()) {
// Setting the background color to clear will also hide the shadow.
2014-12-23 19:17:56 +00:00
[window_ setBackgroundColor:[NSColor clearColor]];
}
2015-04-25 02:35:28 +00:00
if (windowType == "desktop") {
[window_ setLevel:kCGDesktopWindowLevel - 1];
2015-11-20 05:06:42 +00:00
[window_ setDisableKeyOrMainWindow:YES];
2018-04-20 18:47:04 +00:00
[window_ setCollectionBehavior:(NSWindowCollectionBehaviorCanJoinAllSpaces |
NSWindowCollectionBehaviorStationary |
NSWindowCollectionBehaviorIgnoresCycle)];
}
2016-06-13 08:10:28 +00:00
bool focusable;
if (options.Get(options::kFocusable, &focusable) && !focusable)
[window_ setDisableKeyOrMainWindow:YES];
if (transparent() || !has_frame()) {
if (@available(macOS 10.10, *)) {
// Don't show title bar.
2017-06-19 12:34:37 +00:00
[window_ setTitlebarAppearsTransparent:YES];
[window_ setTitleVisibility:NSWindowTitleHidden];
}
// Remove non-transparent corners, see http://git.io/vfonD.
[window_ setOpaque:NO];
}
2014-12-23 19:17:56 +00:00
// Create a tab only if tabbing identifier is specified and window has
// a native title bar.
2017-03-30 20:47:11 +00:00
if (tabbingIdentifier.empty() || transparent() || !has_frame()) {
if (@available(macOS 10.12, *)) {
[window_ setTabbingMode:NSWindowTabbingModeDisallowed];
}
} else {
if (@available(macOS 10.12, *)) {
2017-03-30 20:47:11 +00:00
[window_ setTabbingIdentifier:base::SysUTF8ToNSString(tabbingIdentifier)];
2017-03-27 14:15:17 +00:00
}
}
// Hide the title bar background
if (title_bar_style_ != NORMAL) {
if (@available(macOS 10.10, *)) {
[window_ setTitlebarAppearsTransparent:YES];
}
}
// Hide the title bar.
if (title_bar_style_ == HIDDEN_INSET) {
if (@available(macOS 10.10, *)) {
base::scoped_nsobject<NSToolbar> toolbar(
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
[toolbar setShowsBaselineSeparator:NO];
[window_ setToolbar:toolbar];
} else {
[window_ enableWindowButtonsOffset];
[window_ setWindowButtonsOffset:NSMakePoint(12, 10)];
}
}
2018-04-24 10:00:44 +00:00
// Resize to content bounds.
bool use_content_size = false;
options.Get(options::kUseContentSize, &use_content_size);
2018-04-24 10:00:44 +00:00
if (!has_frame() || use_content_size)
SetContentSize(gfx::Size(width, height));
// Enable the NSView to accept first mouse event.
bool acceptsFirstMouse = false;
options.Get(options::kAcceptFirstMouse, &acceptsFirstMouse);
[window_ setAcceptsFirstMouse:acceptsFirstMouse];
2013-04-12 07:04:46 +00:00
2015-04-27 04:08:22 +00:00
// Disable auto-hiding cursor.
bool disableAutoHideCursor = false;
options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor);
2015-04-27 04:08:22 +00:00
[window_ setDisableAutoHideCursor:disableAutoHideCursor];
2016-01-25 07:02:43 +00:00
// Use an NSEvent monitor to listen for the wheel event.
BOOL __block began = NO;
wheel_event_monitor_ = [NSEvent
2018-04-20 18:47:04 +00:00
addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
handler:^(NSEvent* event) {
if ([[event window] windowNumber] !=
[window_ windowNumber])
return event;
if (!began && (([event phase] ==
NSEventPhaseMayBegin) ||
([event phase] ==
NSEventPhaseBegan))) {
this->NotifyWindowScrollTouchBegin();
began = YES;
} else if (began &&
(([event phase] ==
NSEventPhaseEnded) ||
([event phase] ==
NSEventPhaseCancelled))) {
this->NotifyWindowScrollTouchEnd();
began = NO;
}
return event;
}];
// Set maximizable state last to ensure zoom button does not get reset
// by calls to other APIs.
2016-08-04 23:47:03 +00:00
SetMaximizable(maximizable);
2018-04-10 08:30:46 +00:00
// Default content view.
SetContentView(new views::View());
2018-04-20 18:47:04 +00:00
// 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.
2018-04-24 11:08:14 +00:00
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];
2018-04-10 08:30:46 +00:00
[[window_ contentView] setWantsLayer:YES];
2018-04-24 11:08:14 +00:00
}
if (!has_frame()) {
2018-04-10 08:30:46 +00:00
// 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
2018-05-02 11:43:45 +00:00
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];
}
2018-04-10 08:30:46 +00:00
// 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_];
2018-04-10 08:30:46 +00:00
} 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];
}
2018-04-08 11:20:43 +00:00
}
NativeWindowMac::~NativeWindowMac() {
[NSEvent removeMonitor:wheel_event_monitor_];
}
void NativeWindowMac::SetContentView(views::View* view) {
views::View* root_view = GetContentsView();
if (content_view())
root_view->RemoveChildView(content_view());
set_content_view(view);
root_view->AddChildView(content_view());
if (buttons_view_) {
// Ensure the buttons view are always floated on the top.
[buttons_view_ removeFromSuperview];
[[window_ contentView] addSubview:buttons_view_];
}
root_view->Layout();
}
2013-04-12 07:04:46 +00:00
void NativeWindowMac::Close() {
// When this is a sheet showing, performClose won't work.
if (is_modal() && parent() && IsVisible()) {
[parent()->GetNativeWindow() endSheet:window_];
CloseImmediately();
return;
}
if (!IsClosable()) {
WindowList::WindowCloseCancelled(this);
return;
}
2014-04-11 04:45:48 +00:00
[window_ performClose:nil];
2013-04-12 07:04:46 +00:00
}
2013-05-01 08:12:00 +00:00
void NativeWindowMac::CloseImmediately() {
2014-04-11 04:45:48 +00:00
[window_ close];
2013-05-01 08:12:00 +00:00
}
2013-04-12 07:04:46 +00:00
void NativeWindowMac::Focus(bool focus) {
if (!IsVisible())
return;
if (focus) {
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
2014-04-11 04:45:48 +00:00
[window_ makeKeyAndOrderFront:nil];
} else {
2014-04-11 04:45:48 +00:00
[window_ orderBack:nil];
}
2013-04-12 07:04:46 +00:00
}
2013-05-16 14:56:52 +00:00
bool NativeWindowMac::IsFocused() {
2014-04-11 04:45:48 +00:00
return [window_ isKeyWindow];
2013-05-16 14:56:52 +00:00
}
2013-04-12 07:04:46 +00:00
void NativeWindowMac::Show() {
if (is_modal() && parent()) {
2016-09-29 18:49:24 +00:00
if ([window_ sheetParent] == nil)
[parent()->GetNativeWindow() beginSheet:window_
2018-04-20 18:47:04 +00:00
completionHandler:^(NSModalResponse){
}];
return;
}
2017-09-14 05:43:22 +00:00
// Reattach the window to the parent to actually show it.
if (parent())
InternalSetParentWindow(parent(), true);
// This method is supposed to put focus on window, however if the app does not
// have focus then "makeKeyAndOrderFront" will only show the window.
[NSApp activateIgnoringOtherApps:YES];
2014-10-17 14:51:20 +00:00
[window_ makeKeyAndOrderFront:nil];
}
void NativeWindowMac::ShowInactive() {
2017-05-01 21:01:48 +00:00
// Reattach the window to the parent to actually show it.
if (parent())
InternalSetParentWindow(parent(), true);
2017-09-14 05:43:22 +00:00
[window_ orderFrontRegardless];
2013-04-12 07:04:46 +00:00
}
void NativeWindowMac::Hide() {
if (is_modal() && parent()) {
[window_ orderOut:nil];
[parent()->GetNativeWindow() endSheet:window_];
return;
}
2017-09-14 05:43:22 +00:00
// Deattach the window from the parent before.
if (parent())
InternalSetParentWindow(parent(), false);
2014-04-11 04:45:48 +00:00
[window_ orderOut:nil];
2013-04-12 07:04:46 +00:00
}
2013-10-03 00:27:59 +00:00
bool NativeWindowMac::IsVisible() {
2014-04-11 04:45:48 +00:00
return [window_ isVisible];
2013-10-03 00:27:59 +00:00
}
bool NativeWindowMac::IsEnabled() {
return [window_ attachedSheet] == nil;
}
void NativeWindowMac::SetEnabled(bool enable) {
2018-02-06 13:30:33 +00:00
if (enable) {
2018-04-20 18:47:04 +00:00
[window_ beginSheet:window_
completionHandler:^(NSModalResponse returnCode) {
NSLog(@"modal enabled");
return;
}];
} else {
2018-04-20 18:47:04 +00:00
[window_ endSheet:[window_ attachedSheet]];
}
}
2013-04-12 07:04:46 +00:00
void NativeWindowMac::Maximize() {
if (IsMaximized())
return;
2014-04-11 04:45:48 +00:00
[window_ zoom:nil];
2013-04-12 07:04:46 +00:00
}
void NativeWindowMac::Unmaximize() {
if (!IsMaximized())
return;
2014-04-11 04:45:48 +00:00
[window_ zoom:nil];
2013-04-12 07:04:46 +00:00
}
2014-06-28 01:17:37 +00:00
bool NativeWindowMac::IsMaximized() {
if (([window_ styleMask] & NSResizableWindowMask) != 0) {
return [window_ isZoomed];
} else {
NSRect rectScreen = [[NSScreen mainScreen] visibleFrame];
NSRect rectWindow = [window_ frame];
return (rectScreen.origin.x == rectWindow.origin.x &&
rectScreen.origin.y == rectWindow.origin.y &&
rectScreen.size.width == rectWindow.size.width &&
rectScreen.size.height == rectWindow.size.height);
}
2014-05-14 21:58:49 +00:00
}
2013-04-12 07:04:46 +00:00
void NativeWindowMac::Minimize() {
2014-04-11 04:45:48 +00:00
[window_ miniaturize:nil];
2013-04-12 07:04:46 +00:00
}
void NativeWindowMac::Restore() {
2014-04-11 04:45:48 +00:00
[window_ deminiaturize:nil];
2013-04-12 07:04:46 +00:00
}
2014-07-26 05:58:26 +00:00
bool NativeWindowMac::IsMinimized() {
return [window_ isMiniaturized];
}
2014-11-25 06:34:14 +00:00
void NativeWindowMac::SetFullScreen(bool fullscreen) {
if (fullscreen == IsFullscreen())
2013-04-12 07:04:46 +00:00
return;
[window_ toggleFullScreenMode:nil];
2013-04-12 07:04:46 +00:00
}
2015-04-21 13:35:36 +00:00
bool NativeWindowMac::IsFullscreen() const {
2014-04-11 04:45:48 +00:00
return [window_ styleMask] & NSFullScreenWindowMask;
2013-04-12 07:04:46 +00:00
}
2016-01-15 04:54:12 +00:00
void NativeWindowMac::SetBounds(const gfx::Rect& bounds, bool animate) {
// Do nothing if in fullscreen mode.
if (IsFullscreen())
return;
2016-07-07 11:02:18 +00:00
// Check size constraints since setFrame does not check it.
gfx::Size size = bounds.size();
size.SetToMax(GetMinimumSize());
gfx::Size max_size = GetMaximumSize();
if (!max_size.IsEmpty())
size.SetToMin(max_size);
NSRect cocoa_bounds = NSMakeRect(bounds.x(), 0, size.width(), size.height());
// Flip coordinates based on the primary screen.
NSScreen* screen = [[NSScreen screens] firstObject];
2018-04-20 18:47:04 +00:00
cocoa_bounds.origin.y = NSHeight([screen frame]) - size.height() - bounds.y();
2016-01-15 04:54:12 +00:00
[window_ setFrame:cocoa_bounds display:YES animate:animate];
}
gfx::Rect NativeWindowMac::GetBounds() {
NSRect frame = [window_ frame];
gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
NSScreen* screen = [[NSScreen screens] firstObject];
bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
return bounds;
}
void NativeWindowMac::SetContentSizeConstraints(
const extensions::SizeConstraints& size_constraints) {
auto convertSize = [this](const gfx::Size& size) {
// Our frameless window still has titlebar attached, so setting contentSize
// will result in actual content size being larger.
if (!has_frame()) {
NSRect frame = NSMakeRect(0, 0, size.width(), size.height());
NSRect content = [window_ originalContentRectForFrameRect:frame];
return content.size;
} else {
return NSMakeSize(size.width(), size.height());
}
};
2014-04-11 04:45:48 +00:00
NSView* content = [window_ contentView];
if (size_constraints.HasMinimumSize()) {
NSSize min_size = convertSize(size_constraints.GetMinimumSize());
[window_ setContentMinSize:[content convertSize:min_size toView:nil]];
}
if (size_constraints.HasMaximumSize()) {
NSSize max_size = convertSize(size_constraints.GetMaximumSize());
[window_ setContentMaxSize:[content convertSize:max_size toView:nil]];
}
NativeWindow::SetContentSizeConstraints(size_constraints);
2013-04-18 07:38:04 +00:00
}
2018-04-20 18:47:04 +00:00
void NativeWindowMac::MoveTop() {
[window_ orderWindow:NSWindowAbove relativeTo:0];
}
2018-04-24 10:00:44 +00:00
2013-04-12 07:04:46 +00:00
void NativeWindowMac::SetResizable(bool resizable) {
2016-01-23 11:12:19 +00:00
SetStyleMask(resizable, NSResizableWindowMask);
2013-04-12 07:04:46 +00:00
}
2013-04-18 07:38:04 +00:00
bool NativeWindowMac::IsResizable() {
2014-04-11 04:45:48 +00:00
return [window_ styleMask] & NSResizableWindowMask;
2013-04-18 07:38:04 +00:00
}
void NativeWindowMac::SetAspectRatio(double aspect_ratio,
2016-05-23 01:36:05 +00:00
const gfx::Size& extra_size) {
2016-07-11 07:43:01 +00:00
NativeWindow::SetAspectRatio(aspect_ratio, extra_size);
2016-07-11 07:43:01 +00:00
// Reset the behaviour to default if aspect_ratio is set to 0 or less.
if (aspect_ratio > 0.0)
[window_ setAspectRatio:NSMakeSize(aspect_ratio, 1.0)];
else
[window_ setResizeIncrements:NSMakeSize(1.0, 1.0)];
}
2016-10-26 00:47:22 +00:00
void NativeWindowMac::PreviewFile(const std::string& path,
const std::string& display_name) {
preview_item_.reset([[AtomPreviewItem alloc]
initWithURL:[NSURL fileURLWithPath:base::SysUTF8ToNSString(path)]
title:base::SysUTF8ToNSString(display_name)]);
[[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil];
2016-10-12 01:08:01 +00:00
}
2016-11-21 18:30:13 +00:00
void NativeWindowMac::CloseFilePreview() {
if ([QLPreviewPanel sharedPreviewPanelExists]) {
[[QLPreviewPanel sharedPreviewPanel] close];
}
}
void NativeWindowMac::SetMovable(bool movable) {
[window_ setMovable:movable];
}
bool NativeWindowMac::IsMovable() {
return [window_ isMovable];
}
void NativeWindowMac::SetMinimizable(bool minimizable) {
2016-01-23 11:12:19 +00:00
SetStyleMask(minimizable, NSMiniaturizableWindowMask);
}
bool NativeWindowMac::IsMinimizable() {
return [window_ styleMask] & NSMiniaturizableWindowMask;
}
2016-01-22 21:24:33 +00:00
void NativeWindowMac::SetMaximizable(bool maximizable) {
[[window_ standardWindowButton:NSWindowZoomButton] setEnabled:maximizable];
}
bool NativeWindowMac::IsMaximizable() {
return [[window_ standardWindowButton:NSWindowZoomButton] isEnabled];
}
2016-01-23 07:47:37 +00:00
void NativeWindowMac::SetFullScreenable(bool fullscreenable) {
2018-04-20 18:47:04 +00:00
SetCollectionBehavior(fullscreenable,
NSWindowCollectionBehaviorFullScreenPrimary);
2016-01-23 11:12:19 +00:00
// On EL Capitan this flag is required to hide fullscreen button.
2018-04-20 18:47:04 +00:00
SetCollectionBehavior(!fullscreenable,
NSWindowCollectionBehaviorFullScreenAuxiliary);
2016-01-22 21:24:33 +00:00
}
2016-01-23 07:47:37 +00:00
bool NativeWindowMac::IsFullScreenable() {
2016-01-23 11:12:19 +00:00
NSUInteger collectionBehavior = [window_ collectionBehavior];
return collectionBehavior & NSWindowCollectionBehaviorFullScreenPrimary;
2016-01-22 21:24:33 +00:00
}
void NativeWindowMac::SetClosable(bool closable) {
2016-01-23 11:12:19 +00:00
SetStyleMask(closable, NSClosableWindowMask);
}
bool NativeWindowMac::IsClosable() {
return [window_ styleMask] & NSClosableWindowMask;
}
2018-04-20 18:47:04 +00:00
void NativeWindowMac::SetAlwaysOnTop(bool top,
const std::string& level,
int relativeLevel,
std::string* error) {
2016-09-22 16:22:28 +00:00
int windowLevel = NSNormalWindowLevel;
CGWindowLevel maxWindowLevel = CGWindowLevelForKey(kCGMaximumWindowLevelKey);
CGWindowLevel minWindowLevel = CGWindowLevelForKey(kCGMinimumWindowLevelKey);
2017-02-07 17:32:40 +00:00
2016-09-22 16:22:28 +00:00
if (top) {
if (level == "floating") {
windowLevel = NSFloatingWindowLevel;
} else if (level == "torn-off-menu") {
windowLevel = NSTornOffMenuWindowLevel;
} else if (level == "modal-panel") {
windowLevel = NSModalPanelWindowLevel;
} else if (level == "main-menu") {
windowLevel = NSMainMenuWindowLevel;
} else if (level == "status") {
windowLevel = NSStatusWindowLevel;
} else if (level == "pop-up-menu") {
windowLevel = NSPopUpMenuWindowLevel;
} else if (level == "screen-saver") {
windowLevel = NSScreenSaverWindowLevel;
} else if (level == "dock") {
// Deprecated by macOS, but kept for backwards compatibility
2016-09-22 16:22:28 +00:00
windowLevel = NSDockWindowLevel;
}
}
NSInteger newLevel = windowLevel + relativeLevel;
if (newLevel >= minWindowLevel && newLevel <= maxWindowLevel) {
[window_ setLevel:newLevel];
} else {
2018-04-20 18:47:04 +00:00
*error = std::string([
[NSString stringWithFormat:@"relativeLevel must be between %d and %d",
minWindowLevel, maxWindowLevel] UTF8String]);
}
2013-04-12 07:04:46 +00:00
}
2013-04-18 07:38:04 +00:00
bool NativeWindowMac::IsAlwaysOnTop() {
return [window_ level] != NSNormalWindowLevel;
2013-04-18 07:38:04 +00:00
}
void NativeWindowMac::Center() {
2014-04-11 04:45:48 +00:00
[window_ center];
2013-04-12 07:04:46 +00:00
}
void NativeWindowMac::Invalidate() {
[window_ flushWindow];
2017-02-14 19:09:15 +00:00
[[window_ contentView] setNeedsDisplay:YES];
}
2013-04-12 07:04:46 +00:00
void NativeWindowMac::SetTitle(const std::string& title) {
// For macOS <= 10.9, the setTitleVisibility API is not available, we have
// to avoid calling setTitle for frameless window.
2017-02-07 17:32:40 +00:00
if (!base::mac::IsAtLeastOS10_10() && (transparent() || !has_frame()))
return;
[window_ setTitle:base::SysUTF8ToNSString(title)];
2013-04-12 07:04:46 +00:00
}
2013-04-18 06:30:05 +00:00
std::string NativeWindowMac::GetTitle() {
2018-04-20 18:47:04 +00:00
return base::SysNSStringToUTF8([window_ title]);
;
2013-04-18 06:30:05 +00:00
}
2013-04-12 07:04:46 +00:00
void NativeWindowMac::FlashFrame(bool flash) {
if (flash) {
attention_request_id_ = [NSApp requestUserAttention:NSInformationalRequest];
} else {
[NSApp cancelUserAttentionRequest:attention_request_id_];
attention_request_id_ = 0;
}
}
2018-04-20 18:47:04 +00:00
void NativeWindowMac::SetSkipTaskbar(bool skip) {}
void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
NSWindow* window = GetNativeWindow();
if (simple_fullscreen && !is_simple_fullscreen_) {
is_simple_fullscreen_ = true;
// Take note of the current window size
original_frame_ = [window frame];
simple_fullscreen_options_ = [NSApp currentSystemPresentationOptions];
simple_fullscreen_mask_ = [window styleMask];
2018-04-20 18:47:04 +00:00
// We can simulate the pre-Lion fullscreen by auto-hiding the dock and menu
// bar
NSApplicationPresentationOptions options =
NSApplicationPresentationAutoHideDock +
NSApplicationPresentationAutoHideMenuBar;
[NSApp setPresentationOptions:options];
was_maximizable_ = IsMaximizable();
was_movable_ = IsMovable();
NSRect fullscreenFrame = [window.screen frame];
2018-04-20 18:47:04 +00:00
if (!fullscreen_window_title()) {
// Hide the titlebar
SetStyleMask(false, NSTitledWindowMask);
// Resize the window to accomodate the _entire_ screen size
2018-04-20 18:47:04 +00:00
fullscreenFrame.size.height -=
[[[NSApplication sharedApplication] mainMenu] menuBarHeight];
} else {
// No need to hide the title, but we should still hide the window buttons
[[window standardWindowButton:NSWindowZoomButton] setHidden:YES];
[[window standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
[[window standardWindowButton:NSWindowCloseButton] setHidden:YES];
}
2018-04-20 18:47:04 +00:00
[window setFrame:fullscreenFrame display:YES animate:YES];
// Fullscreen windows can't be resized, minimized, maximized, or moved
SetMinimizable(false);
SetResizable(false);
SetMaximizable(false);
SetMovable(false);
} else if (!simple_fullscreen && is_simple_fullscreen_) {
is_simple_fullscreen_ = false;
2018-04-20 18:47:04 +00:00
if (!fullscreen_window_title()) {
// Restore the titlebar
SetStyleMask(true, NSTitledWindowMask);
} else {
// Show the window buttons
[[window standardWindowButton:NSWindowZoomButton] setHidden:NO];
[[window standardWindowButton:NSWindowMiniaturizeButton] setHidden:NO];
[[window standardWindowButton:NSWindowCloseButton] setHidden:NO];
}
2018-04-20 18:47:04 +00:00
[window setFrame:original_frame_ display:YES animate:YES];
[NSApp setPresentationOptions:simple_fullscreen_options_];
// Restore original style mask
ScopedDisableResize disable_resize;
[window_ setStyleMask:simple_fullscreen_mask_];
// Restore window manipulation abilities
SetMaximizable(was_maximizable_);
SetMovable(was_movable_);
}
}
bool NativeWindowMac::IsSimpleFullScreen() {
return is_simple_fullscreen_;
}
2013-04-12 07:04:46 +00:00
void NativeWindowMac::SetKiosk(bool kiosk) {
if (kiosk && !is_kiosk_) {
kiosk_options_ = [NSApp currentSystemPresentationOptions];
2013-04-12 07:04:46 +00:00
NSApplicationPresentationOptions options =
NSApplicationPresentationHideDock +
NSApplicationPresentationHideMenuBar +
2013-04-12 07:04:46 +00:00
NSApplicationPresentationDisableAppleMenu +
NSApplicationPresentationDisableProcessSwitching +
NSApplicationPresentationDisableForceQuit +
NSApplicationPresentationDisableSessionTermination +
NSApplicationPresentationDisableHideApplication;
[NSApp setPresentationOptions:options];
is_kiosk_ = true;
2017-01-13 23:04:51 +00:00
was_fullscreen_ = IsFullscreen();
2018-04-20 18:47:04 +00:00
if (!was_fullscreen_)
SetFullScreen(true);
} else if (!kiosk && is_kiosk_) {
is_kiosk_ = false;
2018-04-20 18:47:04 +00:00
if (!was_fullscreen_)
SetFullScreen(false);
[NSApp setPresentationOptions:kiosk_options_];
2013-04-12 07:04:46 +00:00
}
}
bool NativeWindowMac::IsKiosk() {
return is_kiosk_;
}
void NativeWindowMac::SetBackgroundColor(SkColor color) {
2018-04-24 11:08:14 +00:00
base::ScopedCFTypeRef<CGColorRef> cgcolor(
skia::CGColorCreateFromSkColor(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();
2018-04-24 11:08:14 +00:00
[[compositor_superview layer] setBackgroundColor:cgcolor];
// When using WebContents as content view, the contentView also has layer.
if ([[window_ contentView] wantsLayer])
[[[window_ contentView] layer] setBackgroundColor:cgcolor];
2015-10-23 03:35:33 +00:00
}
void NativeWindowMac::SetHasShadow(bool has_shadow) {
[window_ setHasShadow:has_shadow];
}
bool NativeWindowMac::HasShadow() {
return [window_ hasShadow];
}
2017-09-29 02:26:02 +00:00
void NativeWindowMac::SetOpacity(const double opacity) {
[window_ setAlphaValue:opacity];
}
double NativeWindowMac::GetOpacity() {
return [window_ alphaValue];
}
void NativeWindowMac::SetRepresentedFilename(const std::string& filename) {
[window_ setRepresentedFilename:base::SysUTF8ToNSString(filename)];
}
std::string NativeWindowMac::GetRepresentedFilename() {
return base::SysNSStringToUTF8([window_ representedFilename]);
}
void NativeWindowMac::SetDocumentEdited(bool edited) {
[window_ setDocumentEdited:edited];
}
bool NativeWindowMac::IsDocumentEdited() {
return [window_ isDocumentEdited];
}
void NativeWindowMac::SetIgnoreMouseEvents(bool ignore, bool forward) {
[window_ setIgnoresMouseEvents:ignore];
if (!ignore) {
SetForwardMouseMessages(NO);
} else {
SetForwardMouseMessages(forward);
}
}
void NativeWindowMac::SetContentProtection(bool enable) {
2018-04-20 18:47:04 +00:00
[window_
setSharingType:enable ? NSWindowSharingNone : NSWindowSharingReadOnly];
}
void NativeWindowMac::SetBrowserView(NativeBrowserView* view) {
if (browser_view()) {
[browser_view()->GetInspectableWebContentsView()->GetNativeView()
Implement initial, experimental BrowserView API Right now, `<webview>` is the only way to embed additional content in a `BrowserWindow`. Unfortunately `<webview>` suffers from a [number of problems](https://github.com/electron/electron/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3Awebview%20). To make matters worse, many of these are upstream Chromium bugs instead of Electron-specific bugs. For us at [Figma](https://www.figma.com), the main issue is very slow performance. Despite the upstream improvements to `<webview>` through the OOPIF work, it is probable that there will continue to be `<webview>`-specific bugs in the future. Therefore, this introduces a `<webview>` alternative to called `BrowserView`, which... - is a thin wrapper around `api::WebContents` (so bugs in `BrowserView` will likely also be bugs in `BrowserWindow` web contents) - is instantiated in the main process like `BrowserWindow` (and unlike `<webview>`, which lives in the DOM of a `BrowserWindow` web contents) - needs to be added to a `BrowserWindow` to display something on the screen This implements the most basic API. The API is expected to evolve and change in the near future and has consequently been marked as experimental. Please do not use this API in production unless you are prepared to deal with breaking changes. In the future, we will want to change the API to support multiple `BrowserView`s per window. We will also want to consider z-ordering auto-resizing, and possibly even nested views.
2017-04-11 17:47:30 +00:00
removeFromSuperview];
set_browser_view(nullptr);
Implement initial, experimental BrowserView API Right now, `<webview>` is the only way to embed additional content in a `BrowserWindow`. Unfortunately `<webview>` suffers from a [number of problems](https://github.com/electron/electron/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3Awebview%20). To make matters worse, many of these are upstream Chromium bugs instead of Electron-specific bugs. For us at [Figma](https://www.figma.com), the main issue is very slow performance. Despite the upstream improvements to `<webview>` through the OOPIF work, it is probable that there will continue to be `<webview>`-specific bugs in the future. Therefore, this introduces a `<webview>` alternative to called `BrowserView`, which... - is a thin wrapper around `api::WebContents` (so bugs in `BrowserView` will likely also be bugs in `BrowserWindow` web contents) - is instantiated in the main process like `BrowserWindow` (and unlike `<webview>`, which lives in the DOM of a `BrowserWindow` web contents) - needs to be added to a `BrowserWindow` to display something on the screen This implements the most basic API. The API is expected to evolve and change in the near future and has consequently been marked as experimental. Please do not use this API in production unless you are prepared to deal with breaking changes. In the future, we will want to change the API to support multiple `BrowserView`s per window. We will also want to consider z-ordering auto-resizing, and possibly even nested views.
2017-04-11 17:47:30 +00:00
}
if (!view) {
Implement initial, experimental BrowserView API Right now, `<webview>` is the only way to embed additional content in a `BrowserWindow`. Unfortunately `<webview>` suffers from a [number of problems](https://github.com/electron/electron/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3Awebview%20). To make matters worse, many of these are upstream Chromium bugs instead of Electron-specific bugs. For us at [Figma](https://www.figma.com), the main issue is very slow performance. Despite the upstream improvements to `<webview>` through the OOPIF work, it is probable that there will continue to be `<webview>`-specific bugs in the future. Therefore, this introduces a `<webview>` alternative to called `BrowserView`, which... - is a thin wrapper around `api::WebContents` (so bugs in `BrowserView` will likely also be bugs in `BrowserWindow` web contents) - is instantiated in the main process like `BrowserWindow` (and unlike `<webview>`, which lives in the DOM of a `BrowserWindow` web contents) - needs to be added to a `BrowserWindow` to display something on the screen This implements the most basic API. The API is expected to evolve and change in the near future and has consequently been marked as experimental. Please do not use this API in production unless you are prepared to deal with breaking changes. In the future, we will want to change the API to support multiple `BrowserView`s per window. We will also want to consider z-ordering auto-resizing, and possibly even nested views.
2017-04-11 17:47:30 +00:00
return;
}
set_browser_view(view);
auto* native_view = view->GetInspectableWebContentsView()->GetNativeView();
Implement initial, experimental BrowserView API Right now, `<webview>` is the only way to embed additional content in a `BrowserWindow`. Unfortunately `<webview>` suffers from a [number of problems](https://github.com/electron/electron/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3Awebview%20). To make matters worse, many of these are upstream Chromium bugs instead of Electron-specific bugs. For us at [Figma](https://www.figma.com), the main issue is very slow performance. Despite the upstream improvements to `<webview>` through the OOPIF work, it is probable that there will continue to be `<webview>`-specific bugs in the future. Therefore, this introduces a `<webview>` alternative to called `BrowserView`, which... - is a thin wrapper around `api::WebContents` (so bugs in `BrowserView` will likely also be bugs in `BrowserWindow` web contents) - is instantiated in the main process like `BrowserWindow` (and unlike `<webview>`, which lives in the DOM of a `BrowserWindow` web contents) - needs to be added to a `BrowserWindow` to display something on the screen This implements the most basic API. The API is expected to evolve and change in the near future and has consequently been marked as experimental. Please do not use this API in production unless you are prepared to deal with breaking changes. In the future, we will want to change the API to support multiple `BrowserView`s per window. We will also want to consider z-ordering auto-resizing, and possibly even nested views.
2017-04-11 17:47:30 +00:00
[[window_ contentView] addSubview:native_view
positioned:NSWindowAbove
relativeTo:nil];
native_view.hidden = NO;
}
void NativeWindowMac::SetParentWindow(NativeWindow* parent) {
InternalSetParentWindow(parent, IsVisible());
}
2017-05-23 09:41:59 +00:00
gfx::NativeView NativeWindowMac::GetNativeView() const {
return [window_ contentView];
}
2017-09-14 05:43:22 +00:00
gfx::NativeWindow NativeWindowMac::GetNativeWindow() const {
2014-04-11 04:45:48 +00:00
return window_;
}
2017-05-23 09:41:59 +00:00
gfx::AcceleratedWidget NativeWindowMac::GetAcceleratedWidget() const {
return gfx::kNullAcceleratedWidget;
2016-01-07 20:38:35 +00:00
}
2018-04-20 18:47:04 +00:00
void NativeWindowMac::SetProgressBar(double progress,
const NativeWindow::ProgressState state) {
2014-09-17 07:58:08 +00:00
NSDockTile* dock_tile = [NSApp dockTile];
2018-04-20 18:47:04 +00:00
// For the first time API invoked, we need to create a ContentView in
// DockTile.
2017-03-30 20:12:14 +00:00
if (dock_tile.contentView == nullptr) {
2014-09-17 07:58:08 +00:00
NSImageView* image_view = [[NSImageView alloc] init];
[image_view setImage:[NSApp applicationIconImage]];
[dock_tile setContentView:image_view];
}
2014-09-17 07:58:08 +00:00
if ([[dock_tile.contentView subviews] count] == 0) {
2014-09-17 07:58:08 +00:00
NSProgressIndicator* progress_indicator = [[AtomProgressBar alloc]
initWithFrame:NSMakeRect(0.0f, 0.0f, dock_tile.size.width, 15.0)];
[progress_indicator setStyle:NSProgressIndicatorBarStyle];
[progress_indicator setIndeterminate:NO];
[progress_indicator setBezeled:YES];
[progress_indicator setMinValue:0];
[progress_indicator setMaxValue:1];
[progress_indicator setHidden:NO];
[dock_tile.contentView addSubview:progress_indicator];
2014-09-17 07:58:08 +00:00
}
2018-04-20 18:47:04 +00:00
NSProgressIndicator* progress_indicator = static_cast<NSProgressIndicator*>(
[[[dock_tile contentView] subviews] objectAtIndex:0]);
2014-09-17 07:58:08 +00:00
if (progress < 0) {
[progress_indicator setHidden:YES];
} else if (progress > 1) {
[progress_indicator setHidden:NO];
2014-09-17 07:58:08 +00:00
[progress_indicator setIndeterminate:YES];
[progress_indicator setDoubleValue:1];
2014-09-17 07:58:08 +00:00
} else {
[progress_indicator setHidden:NO];
2014-09-17 07:58:08 +00:00
[progress_indicator setDoubleValue:progress];
}
[dock_tile display];
}
2015-02-11 01:14:26 +00:00
void NativeWindowMac::SetOverlayIcon(const gfx::Image& overlay,
2018-04-20 18:47:04 +00:00
const std::string& description) {}
2015-02-07 19:56:03 +00:00
void NativeWindowMac::SetVisibleOnAllWorkspaces(bool visible) {
2016-01-23 11:12:19 +00:00
SetCollectionBehavior(visible, NSWindowCollectionBehaviorCanJoinAllSpaces);
}
bool NativeWindowMac::IsVisibleOnAllWorkspaces() {
2015-03-27 11:41:07 +00:00
NSUInteger collectionBehavior = [window_ collectionBehavior];
return collectionBehavior & NSWindowCollectionBehaviorCanJoinAllSpaces;
}
void NativeWindowMac::SetAutoHideCursor(bool auto_hide) {
[window_ setDisableAutoHideCursor:!auto_hide];
}
void NativeWindowMac::SelectPreviousTab() {
if (@available(macOS 10.12, *)) {
[window_ selectPreviousTab:nil];
}
}
void NativeWindowMac::SelectNextTab() {
if (@available(macOS 10.12, *)) {
[window_ selectNextTab:nil];
}
}
void NativeWindowMac::MergeAllWindows() {
if (@available(macOS 10.12, *)) {
[window_ mergeAllWindows:nil];
}
}
void NativeWindowMac::MoveTabToNewWindow() {
if (@available(macOS 10.12, *)) {
[window_ moveTabToNewWindow:nil];
}
}
void NativeWindowMac::ToggleTabBar() {
if (@available(macOS 10.12, *)) {
[window_ toggleTabBar:nil];
}
}
bool NativeWindowMac::AddTabbedWindow(NativeWindow* window) {
2018-04-24 08:23:08 +00:00
if (window_ == window->GetNativeWindow()) {
return false;
} else {
if (@available(macOS 10.12, *))
[window_ addTabbedWindow:window->GetNativeWindow() ordered:NSWindowAbove];
}
return true;
}
2016-11-07 20:22:41 +00:00
void NativeWindowMac::SetVibrancy(const std::string& type) {
if (@available(macOS 10.10, *)) {
NSView* vibrant_view = [window_ vibrantView];
2016-11-07 20:22:41 +00:00
if (type.empty()) {
if (background_color_before_vibrancy_) {
[window_ setBackgroundColor:background_color_before_vibrancy_];
[window_ setTitlebarAppearsTransparent:transparency_before_vibrancy_];
}
2018-04-20 18:47:04 +00:00
if (vibrant_view == nil)
return;
[vibrant_view removeFromSuperview];
[window_ setVibrantView:nil];
ui::GpuSwitchingManager::SetTransparent(transparent());
return;
}
background_color_before_vibrancy_.reset([[window_ backgroundColor] retain]);
transparency_before_vibrancy_ = [window_ titlebarAppearsTransparent];
ui::GpuSwitchingManager::SetTransparent(true);
if (title_bar_style_ != NORMAL) {
[window_ setTitlebarAppearsTransparent:YES];
[window_ setBackgroundColor:[NSColor clearColor]];
}
NSVisualEffectView* effect_view = (NSVisualEffectView*)vibrant_view;
if (effect_view == nil) {
effect_view = [[[NSVisualEffectView alloc]
2018-04-20 18:47:04 +00:00
initWithFrame:[[window_ contentView] bounds]] autorelease];
[window_ setVibrantView:(NSView*)effect_view];
2018-04-20 18:47:04 +00:00
[effect_view
setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[effect_view setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
[effect_view setState:NSVisualEffectStateActive];
[[window_ contentView] addSubview:effect_view
positioned:NSWindowBelow
relativeTo:nil];
}
2016-11-07 20:22:41 +00:00
NSVisualEffectMaterial vibrancyType = NSVisualEffectMaterialLight;
2016-11-07 20:22:41 +00:00
if (type == "appearance-based") {
vibrancyType = NSVisualEffectMaterialAppearanceBased;
} else if (type == "light") {
vibrancyType = NSVisualEffectMaterialLight;
} else if (type == "dark") {
vibrancyType = NSVisualEffectMaterialDark;
} else if (type == "titlebar") {
vibrancyType = NSVisualEffectMaterialTitlebar;
}
2016-11-07 20:22:41 +00:00
if (@available(macOS 10.11, *)) {
// TODO(kevinsawicki): Use NSVisualEffectMaterial* constants directly once
// they are available in the minimum SDK version
if (type == "selection") {
// NSVisualEffectMaterialSelection
vibrancyType = static_cast<NSVisualEffectMaterial>(4);
} else if (type == "menu") {
// NSVisualEffectMaterialMenu
vibrancyType = static_cast<NSVisualEffectMaterial>(5);
} else if (type == "popover") {
// NSVisualEffectMaterialPopover
vibrancyType = static_cast<NSVisualEffectMaterial>(6);
} else if (type == "sidebar") {
// NSVisualEffectMaterialSidebar
vibrancyType = static_cast<NSVisualEffectMaterial>(7);
} else if (type == "medium-light") {
// NSVisualEffectMaterialMediumLight
vibrancyType = static_cast<NSVisualEffectMaterial>(8);
} else if (type == "ultra-dark") {
// NSVisualEffectMaterialUltraDark
vibrancyType = static_cast<NSVisualEffectMaterial>(9);
}
2016-11-07 20:22:41 +00:00
}
[effect_view setMaterial:vibrancyType];
}
2016-11-07 20:22:41 +00:00
}
void NativeWindowMac::SetTouchBar(
const std::vector<mate::PersistentDictionary>& items) {
2018-04-20 10:23:21 +00:00
if (@available(macOS 10.12.2, *)) {
touch_bar_.reset([[AtomTouchBar alloc]
initWithDelegate:window_delegate_.get()
window:this
settings:items]);
[window_ setTouchBar:nil];
}
}
2017-03-01 00:08:12 +00:00
void NativeWindowMac::RefreshTouchBarItem(const std::string& item_id) {
2018-04-20 10:23:21 +00:00
if (@available(macOS 10.12.2, *)) {
if (touch_bar_ && [window_ touchBar])
[touch_bar_ refreshTouchBarItem:[window_ touchBar] id:item_id];
}
}
void NativeWindowMac::SetEscapeTouchBarItem(
const mate::PersistentDictionary& item) {
2018-04-20 10:23:21 +00:00
if (@available(macOS 10.12.2, *)) {
if (touch_bar_ && [window_ touchBar])
[touch_bar_ setEscapeTouchBarItem:item forTouchBar:[window_ touchBar]];
}
}
gfx::Rect NativeWindowMac::ContentBoundsToWindowBounds(
const gfx::Rect& bounds) const {
if (has_frame()) {
gfx::Rect window_bounds(
[window_ frameRectForContentRect:bounds.ToCGRect()]);
int frame_height = window_bounds.height() - bounds.height();
window_bounds.set_y(window_bounds.y() - frame_height);
return window_bounds;
} else {
return bounds;
}
}
gfx::Rect NativeWindowMac::WindowBoundsToContentBounds(
const gfx::Rect& bounds) const {
if (has_frame()) {
gfx::Rect content_bounds(
[window_ contentRectForFrameRect:bounds.ToCGRect()]);
int frame_height = bounds.height() - content_bounds.height();
content_bounds.set_y(content_bounds.y() + frame_height);
return content_bounds;
} else {
return bounds;
}
}
2018-04-24 10:00:44 +00:00
bool NativeWindowMac::CanResize() const {
return resizable_;
}
views::View* NativeWindowMac::GetContentsView() {
return root_view_.get();
}
2018-04-20 18:47:04 +00:00
void NativeWindowMac::InternalSetParentWindow(NativeWindow* parent,
bool attach) {
2017-09-14 05:43:22 +00:00
if (is_modal())
return;
NativeWindow::SetParentWindow(parent);
// Do not remove/add if we are already properly attached.
if (attach && parent && [window_ parentWindow] == parent->GetNativeWindow())
return;
// Remove current parent window.
if ([window_ parentWindow])
[[window_ parentWindow] removeChildWindow:window_];
// Set new parent window.
// Note that this method will force the window to become visible.
if (parent && attach)
[parent->GetNativeWindow() addChildWindow:window_ ordered:NSWindowAbove];
}
2017-06-05 19:50:18 +00:00
void NativeWindowMac::ShowWindowButton(NSWindowButton button) {
auto view = [window_ standardWindowButton:button];
[view.superview addSubview:view positioned:NSWindowAbove relativeTo:nil];
}
void NativeWindowMac::SetForwardMouseMessages(bool forward) {
[window_ setAcceptsMouseMovedEvents:forward];
}
2016-01-23 11:12:19 +00:00
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.
ScopedDisableResize disable_resize;
2016-08-04 23:47:03 +00:00
bool was_maximizable = IsMaximizable();
2016-01-23 11:12:19 +00:00
if (on)
[window_ setStyleMask:[window_ styleMask] | flag];
else
[window_ setStyleMask:[window_ styleMask] & (~flag)];
// Change style mask will make the zoom button revert to default, probably
2016-06-18 13:26:26 +00:00
// a bug of Cocoa or macOS.
2016-08-04 23:47:03 +00:00
SetMaximizable(was_maximizable);
2016-01-23 11:12:19 +00:00
}
void NativeWindowMac::SetCollectionBehavior(bool on, NSUInteger flag) {
2016-08-04 23:47:03 +00:00
bool was_maximizable = IsMaximizable();
2016-01-23 11:12:19 +00:00
if (on)
[window_ setCollectionBehavior:[window_ collectionBehavior] | flag];
else
[window_ setCollectionBehavior:[window_ collectionBehavior] & (~flag)];
// Change collectionBehavior will make the zoom button revert to default,
2016-06-18 13:26:26 +00:00
// probably a bug of Cocoa or macOS.
2016-08-04 23:47:03 +00:00
SetMaximizable(was_maximizable);
2016-01-23 11:12:19 +00:00
}
2013-04-12 07:04:46 +00:00
// static
2018-04-08 11:20:43 +00:00
NativeWindow* NativeWindow::Create(const mate::Dictionary& options,
NativeWindow* parent) {
return new NativeWindowMac(options, parent);
2013-04-12 07:04:46 +00:00
}
} // namespace atom