electron/atom/browser/native_window_mac.mm

2051 lines
64 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>
2016-10-13 00:21:56 +00:00
#include <Quartz/Quartz.h>
2016-10-26 00:55:34 +00:00
#include <string>
#include "atom/browser/browser.h"
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"
#include "atom/browser/ui/cocoa/atom_touch_bar.h"
#include "atom/browser/window_list.h"
#include "atom/common/color_util.h"
2014-03-16 00:30:26 +00:00
#include "atom/common/draggable_region.h"
#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"
#include "brightray/browser/mac/event_dispatching_window.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
2015-04-13 12:10:51 +00:00
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
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 "content/public/browser/web_contents.h"
#include "native_mate/dictionary.h"
#include "skia/ext/skia_utils_mac.h"
2016-09-06 08:24:37 +00:00
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/gfx/skia_util.h"
2013-04-12 07:04:46 +00:00
namespace {
// Prevents window from resizing during the scope.
class ScopedDisableResize {
public:
ScopedDisableResize() { disable_resize_ = true; }
~ScopedDisableResize() { disable_resize_ = false; }
static bool IsResizeDisabled() { return disable_resize_; }
private:
static bool disable_resize_;
};
bool ScopedDisableResize::disable_resize_ = false;
} // namespace
// 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
// 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
@interface AtomNSWindowDelegate : NSObject<NSWindowDelegate> {
@private
atom::NativeWindowMac* shell_;
bool is_zooming_;
int level_;
bool is_resizable_;
}
- (id)initWithShell:(atom::NativeWindowMac*)shell;
@end
@implementation AtomNSWindowDelegate
- (id)initWithShell:(atom::NativeWindowMac*)shell {
if ((self = [super init])) {
shell_ = shell;
is_zooming_ = false;
2017-07-14 17:07:06 +00:00
level_ = [shell_->GetNativeWindow() level];
}
return self;
}
- (void)windowDidChangeOcclusionState:(NSNotification *)notification {
// notification.object is the window that changed its state.
// It's safe to use self.window instead if you don't assign one delegate to many windows
NSWindow *window = notification.object;
// check occlusion binary flag
if (window.occlusionState & NSWindowOcclusionStateVisible) {
// The app is visible
shell_->NotifyWindowShow();
} else {
// The app is not visible
shell_->NotifyWindowHide();
}
}
// Called when the user clicks the zoom button or selects it from the Window
2016-10-27 18:49:13 +00:00
// menu to determine the "standard size" of the window.
- (NSRect)windowWillUseStandardFrame:(NSWindow*)window
defaultFrame:(NSRect)frame {
2016-10-27 18:29:51 +00:00
if (!shell_->zoom_to_page_width())
return frame;
// If the shift key is down, maximize.
if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask)
return frame;
content::WebContents* web_contents = shell_->web_contents();
2016-10-27 18:49:13 +00:00
if (!web_contents)
return frame;
2016-10-27 19:13:45 +00:00
CGFloat page_width = static_cast<CGFloat>(
2016-10-27 18:49:13 +00:00
web_contents->GetPreferredSize().width());
2016-10-27 19:13:45 +00:00
NSRect window_frame = [window frame];
2016-10-27 18:49:13 +00:00
// Never shrink from the current size on zoom.
2016-10-27 19:13:45 +00:00
CGFloat zoomed_width = std::max(page_width, NSWidth(window_frame));
// |frame| determines our maximum extents. We need to set the origin of the
// frame -- and only move it left if necessary.
2016-10-27 19:13:45 +00:00
if (window_frame.origin.x + zoomed_width > NSMaxX(frame))
frame.origin.x = NSMaxX(frame) - zoomed_width;
else
2016-10-27 19:13:45 +00:00
frame.origin.x = window_frame.origin.x;
// Set the width. Don't touch y or height.
2016-10-27 19:13:45 +00:00
frame.size.width = zoomed_width;
return frame;
}
- (void)windowDidBecomeMain:(NSNotification*)notification {
2015-06-25 06:54:00 +00:00
content::WebContents* web_contents = shell_->web_contents();
if (!web_contents)
return;
2014-07-28 07:29:51 +00:00
web_contents->RestoreFocus();
content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
if (rwhv)
rwhv->SetActive(true);
shell_->NotifyWindowFocus();
}
2013-05-24 10:15:27 +00:00
- (void)windowDidResignMain:(NSNotification*)notification {
2015-06-25 06:54:00 +00:00
content::WebContents* web_contents = shell_->web_contents();
if (!web_contents)
return;
2014-07-28 07:29:51 +00:00
web_contents->StoreFocus();
content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
if (rwhv)
rwhv->SetActive(false);
shell_->NotifyWindowBlur();
}
- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize {
NSSize newSize = frameSize;
double aspectRatio = shell_->GetAspectRatio();
if (aspectRatio > 0.0) {
gfx::Size windowSize = shell_->GetSize();
gfx::Size contentSize = shell_->GetContentSize();
gfx::Size extraSize = shell_->GetAspectRatioExtraSize();
double extraWidthPlusFrame =
windowSize.width() - contentSize.width() + extraSize.width();
double extraHeightPlusFrame =
windowSize.height() - contentSize.height() + extraSize.height();
newSize.width =
roundf((frameSize.height - extraHeightPlusFrame) * aspectRatio +
extraWidthPlusFrame);
newSize.height =
roundf((newSize.width - extraWidthPlusFrame) / aspectRatio +
extraHeightPlusFrame);
}
return newSize;
}
- (void)windowDidResize:(NSNotification*)notification {
shell_->UpdateDraggableRegionViews();
2015-05-09 15:55:10 +00:00
shell_->NotifyWindowResize();
}
- (void)windowDidMove:(NSNotification*)notification {
2015-05-20 08:37:13 +00:00
// TODO(zcbenz): Remove the alias after figuring out a proper
// way to dispatch move.
2015-05-09 15:55:10 +00:00
shell_->NotifyWindowMove();
2015-05-20 08:37:13 +00:00
shell_->NotifyWindowMoved();
}
- (void)windowWillMiniaturize:(NSNotification*)notification {
NSWindow* window = shell_->GetNativeWindow();
2017-06-07 23:01:06 +00:00
// store the current status window level to be restored in windowDidDeminiaturize
2017-06-15 22:05:58 +00:00
level_ = [window level];
[window setLevel:NSNormalWindowLevel];
}
2014-11-25 05:16:51 +00:00
- (void)windowDidMiniaturize:(NSNotification*)notification {
shell_->NotifyWindowMinimize();
}
- (void)windowDidDeminiaturize:(NSNotification*)notification {
2017-07-14 17:07:06 +00:00
[shell_->GetNativeWindow() setLevel:level_];
2014-11-25 05:16:51 +00:00
shell_->NotifyWindowRestore();
}
- (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame {
is_zooming_ = true;
2014-11-25 05:16:51 +00:00
return YES;
}
- (void)windowDidEndLiveResize:(NSNotification*)notification {
if (is_zooming_) {
if (shell_->IsMaximized())
shell_->NotifyWindowMaximize();
else
shell_->NotifyWindowUnmaximize();
is_zooming_ = false;
}
}
- (void)windowWillEnterFullScreen:(NSNotification*)notification {
// Setting resizable to true before entering fullscreen
is_resizable_ = shell_->IsResizable();
shell_->SetResizable(true);
// Hide the native toolbar before entering fullscreen, so there is no visual
// artifacts.
2017-02-07 17:32:40 +00:00
if (base::mac::IsAtLeastOS10_10() &&
shell_->title_bar_style() == atom::NativeWindowMac::HIDDEN_INSET) {
NSWindow* window = shell_->GetNativeWindow();
[window setToolbar:nil];
}
2014-11-25 05:16:51 +00:00
}
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
shell_->NotifyWindowEnterFullScreen();
// For frameless window we don't show set title for normal mode since the
// titlebar is expected to be empty, but after entering fullscreen mode we
// have to set one, because title bar is visible here.
NSWindow* window = shell_->GetNativeWindow();
if ((shell_->transparent() || !shell_->has_frame()) &&
2017-02-07 17:32:40 +00:00
base::mac::IsAtLeastOS10_10() &&
// FIXME(zcbenz): Showing titlebar for hiddenInset window is weird under
// fullscreen mode.
// Show title if fullscreen_window_title flag is set
(shell_->title_bar_style() != atom::NativeWindowMac::HIDDEN_INSET ||
shell_->fullscreen_window_title())) {
[window setTitleVisibility:NSWindowTitleVisible];
}
// Restore the native toolbar immediately after entering fullscreen, if we do
// this before leaving fullscreen, traffic light buttons will be jumping.
2017-02-07 17:32:40 +00:00
if (base::mac::IsAtLeastOS10_10() &&
shell_->title_bar_style() == atom::NativeWindowMac::HIDDEN_INSET) {
base::scoped_nsobject<NSToolbar> toolbar(
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
[toolbar setShowsBaselineSeparator:NO];
[window setToolbar:toolbar];
// Set window style to hide the toolbar, otherwise the toolbar will show in
// fullscreen mode.
shell_->SetStyleMask(true, NSFullSizeContentViewWindowMask);
}
}
- (void)windowWillExitFullScreen:(NSNotification*)notification {
// Restore the titlebar visibility.
NSWindow* window = shell_->GetNativeWindow();
if ((shell_->transparent() || !shell_->has_frame()) &&
2017-02-07 17:32:40 +00:00
base::mac::IsAtLeastOS10_10() &&
(shell_->title_bar_style() != atom::NativeWindowMac::HIDDEN_INSET ||
shell_->fullscreen_window_title())) {
[window setTitleVisibility:NSWindowTitleHidden];
}
// Turn off the style for toolbar.
2017-02-07 17:32:40 +00:00
if (base::mac::IsAtLeastOS10_10() &&
shell_->title_bar_style() == atom::NativeWindowMac::HIDDEN_INSET) {
shell_->SetStyleMask(false, NSFullSizeContentViewWindowMask);
}
}
- (void)windowDidExitFullScreen:(NSNotification*)notification {
shell_->SetResizable(is_resizable_);
2014-11-25 05:16:51 +00:00
shell_->NotifyWindowLeaveFullScreen();
}
- (void)windowWillClose:(NSNotification*)notification {
shell_->NotifyWindowClosed();
// Clears the delegate when window is going to be closed, since EL Capitan it
// is possible that the methods of delegate would get called after the window
// has been closed.
[shell_->GetNativeWindow() setDelegate:nil];
2013-05-01 08:12:00 +00:00
}
2013-05-01 08:12:00 +00:00
- (BOOL)windowShouldClose:(id)window {
// When user tries to close the window by clicking the close button, we do
// not close the window immediately, instead we try to close the web page
2017-06-05 19:50:18 +00:00
// first, and when the web page is closed the window will also be closed.
shell_->RequestToClosePage();
2013-05-01 08:12:00 +00:00
return NO;
}
2016-04-20 00:18:50 +00:00
- (NSRect)window:(NSWindow*)window
willPositionSheet:(NSWindow*)sheet usingRect:(NSRect)rect {
NSView* view = window.contentView;
rect.origin.x = shell_->GetSheetOffsetX();
rect.origin.y = view.frame.size.height - shell_->GetSheetOffsetY();
return rect;
}
- (void)windowWillBeginSheet:(NSNotification *)notification {
shell_->NotifyWindowSheetBegin();
}
- (void)windowDidEndSheet:(NSNotification *)notification {
shell_->NotifyWindowSheetEnd();
}
- (IBAction)newWindowForTab:(id)sender {
shell_->NotifyNewWindowForTab();
atom::Browser::Get()->NewWindowForTab();
}
@end
2016-10-13 00:21:56 +00:00
@interface AtomPreviewItem : NSObject <QLPreviewItem>
2016-10-24 18:49:30 +00:00
@property (nonatomic, retain) NSURL* previewItemURL;
@property (nonatomic, retain) NSString* previewItemTitle;
2016-10-13 00:21:56 +00:00
2016-10-24 18:49:30 +00:00
- (id)initWithURL:(NSURL*)url title:(NSString*)title;
2016-10-13 00:21:56 +00:00
@end
@implementation AtomPreviewItem
2016-10-24 18:49:30 +00:00
- (id)initWithURL:(NSURL*)url title:(NSString*)title {
2016-10-26 00:47:22 +00:00
self = [super init];
2016-10-13 00:21:56 +00:00
if (self) {
self.previewItemURL = url;
self.previewItemTitle = title;
2016-10-26 00:47:22 +00:00
}
2016-10-13 00:21:56 +00:00
return self;
}
@end
2017-10-05 22:57:27 +00:00
#if !defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER)
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
@interface AtomNSWindow : EventDispatchingWindow<QLPreviewPanelDataSource, QLPreviewPanelDelegate, NSTouchBarDelegate> {
@private
atom::NativeWindowMac* shell_;
bool enable_larger_than_screen_;
2017-02-28 21:54:20 +00:00
base::scoped_nsobject<AtomTouchBar> atom_touch_bar_;
CGFloat windowButtonsInterButtonSpacing_;
}
@property BOOL acceptsFirstMouse;
2015-04-27 04:08:22 +00:00
@property BOOL disableAutoHideCursor;
@property BOOL disableKeyOrMainWindow;
@property NSPoint windowButtonsOffset;
2016-10-13 00:21:56 +00:00
@property (nonatomic, retain) AtomPreviewItem* quickLookItem;
@property (nonatomic, retain) NSView* vibrantView;
- (void)setShell:(atom::NativeWindowMac*)shell;
- (void)setEnableLargerThanScreen:(bool)enable;
- (void)enableWindowButtonsOffset;
2017-03-01 23:26:10 +00:00
- (void)resetTouchBar:(const std::vector<mate::PersistentDictionary>&)settings;
2017-03-01 00:08:12 +00:00
- (void)refreshTouchBarItem:(const std::string&)item_id;
2017-03-29 19:45:01 +00:00
- (void)setEscapeTouchBarItem:(const mate::PersistentDictionary&)item;
2017-03-01 00:08:12 +00:00
@end
@implementation AtomNSWindow
- (void)setShell:(atom::NativeWindowMac*)shell {
shell_ = shell;
}
- (void)setEnableLargerThanScreen:(bool)enable {
enable_larger_than_screen_ = enable;
}
2017-03-01 23:26:10 +00:00
- (void)resetTouchBar:(const std::vector<mate::PersistentDictionary>&)settings {
if (![self respondsToSelector:@selector(touchBar)]) return;
2017-03-01 23:26:10 +00:00
atom_touch_bar_.reset([[AtomTouchBar alloc] initWithDelegate:self
window:shell_
settings:settings]);
2017-02-27 18:49:45 +00:00
self.touchBar = nil;
}
2017-03-01 00:08:12 +00:00
- (void)refreshTouchBarItem:(const std::string&)item_id {
if (atom_touch_bar_ && self.touchBar)
[atom_touch_bar_ refreshTouchBarItem:self.touchBar id:item_id];
}
- (NSTouchBar*)makeTouchBar {
2017-03-01 23:29:34 +00:00
if (atom_touch_bar_)
return [atom_touch_bar_ makeTouchBar];
else
return nil;
}
2017-03-03 22:07:59 +00:00
- (NSTouchBarItem*)touchBar:(NSTouchBar*)touchBar
makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier {
2017-03-01 23:29:34 +00:00
if (touchBar && atom_touch_bar_)
return [atom_touch_bar_ makeItemForIdentifier:identifier];
else
return nil;
2016-11-29 07:36:57 +00:00
}
2017-03-29 19:45:01 +00:00
- (void)setEscapeTouchBarItem:(const mate::PersistentDictionary&)item {
2017-03-29 04:11:39 +00:00
if (atom_touch_bar_ && self.touchBar)
[atom_touch_bar_ setEscapeTouchBarItem:item forTouchBar:self.touchBar];
}
2015-12-07 11:56:23 +00:00
// NSWindow overrides.
2017-02-28 20:33:56 +00:00
- (void)swipeWithEvent:(NSEvent *)event {
if (event.deltaY == 1.0) {
shell_->NotifyWindowSwipe("up");
} else if (event.deltaX == -1.0) {
shell_->NotifyWindowSwipe("right");
} else if (event.deltaY == -1.0) {
shell_->NotifyWindowSwipe("down");
} else if (event.deltaX == 1.0) {
shell_->NotifyWindowSwipe("left");
}
}
- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen*)screen {
// Resizing is disabled.
if (ScopedDisableResize::IsResizeDisabled())
return [self frame];
// Enable the window to be larger than screen.
if (enable_larger_than_screen_)
return frameRect;
else
return [super constrainFrameRect:frameRect toScreen:screen];
}
- (void)setFrame:(NSRect)windowFrame display:(BOOL)displayViews {
// constrainFrameRect is not called on hidden windows so disable adjusting
// the frame directly when resize is disabled
if (!ScopedDisableResize::IsResizeDisabled())
[super setFrame:windowFrame display:displayViews];
}
- (id)accessibilityAttributeValue:(NSString*)attribute {
2015-04-13 12:10:51 +00:00
if (![attribute isEqualToString:@"AXChildren"])
return [super accessibilityAttributeValue:attribute];
// Filter out objects that aren't the title bar buttons. This has the effect
// of removing the window title, which VoiceOver already sees.
// * when VoiceOver is disabled, this causes Cmd+C to be used for TTS but
// still leaves the buttons available in the accessibility tree.
// * when VoiceOver is enabled, the full accessibility tree is used.
// Without removing the title and with VO disabled, the TTS would always read
// the window title instead of using Cmd+C to get the selected text.
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"(self isKindOfClass: %@) OR (self.className == %@)",
[NSButtonCell class],
@"RenderWidgetHostViewCocoa"];
NSArray *children = [super accessibilityAttributeValue:attribute];
return [children filteredArrayUsingPredicate:predicate];
}
2015-11-20 05:06:42 +00:00
- (BOOL)canBecomeMainWindow {
return !self.disableKeyOrMainWindow;
}
2015-11-20 05:06:42 +00:00
- (BOOL)canBecomeKeyWindow {
return !self.disableKeyOrMainWindow;
}
- (void)enableWindowButtonsOffset {
auto closeButton = [self standardWindowButton:NSWindowCloseButton];
auto miniaturizeButton = [self standardWindowButton:NSWindowMiniaturizeButton];
auto zoomButton = [self standardWindowButton:NSWindowZoomButton];
[closeButton setPostsFrameChangedNotifications:YES];
[miniaturizeButton setPostsFrameChangedNotifications:YES];
[zoomButton setPostsFrameChangedNotifications:YES];
windowButtonsInterButtonSpacing_ =
NSMinX([miniaturizeButton frame]) - NSMaxX([closeButton frame]);
auto center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(adjustCloseButton:)
name:NSViewFrameDidChangeNotification
object:closeButton];
[center addObserver:self
selector:@selector(adjustMiniaturizeButton:)
name:NSViewFrameDidChangeNotification
object:miniaturizeButton];
[center addObserver:self
selector:@selector(adjustZoomButton:)
name:NSViewFrameDidChangeNotification
object:zoomButton];
}
- (void)adjustCloseButton:(NSNotification*)notification {
[self adjustButton:[notification object]
ofKind:NSWindowCloseButton];
}
- (void)adjustMiniaturizeButton:(NSNotification*)notification {
[self adjustButton:[notification object]
ofKind:NSWindowMiniaturizeButton];
}
- (void)adjustZoomButton:(NSNotification*)notification {
[self adjustButton:[notification object]
ofKind:NSWindowZoomButton];
}
- (void)adjustButton:(NSButton*)button
ofKind:(NSWindowButton)kind {
NSRect buttonFrame = [button frame];
NSRect frameViewBounds = [[self frameView] bounds];
NSPoint offset = self.windowButtonsOffset;
buttonFrame.origin = NSMakePoint(
offset.x,
(NSHeight(frameViewBounds) - NSHeight(buttonFrame) - offset.y));
switch (kind) {
case NSWindowZoomButton:
buttonFrame.origin.x += NSWidth(
[[self standardWindowButton:NSWindowMiniaturizeButton] frame]);
buttonFrame.origin.x += windowButtonsInterButtonSpacing_;
// fallthrough
case NSWindowMiniaturizeButton:
buttonFrame.origin.x += NSWidth(
[[self standardWindowButton:NSWindowCloseButton] frame]);
buttonFrame.origin.x += windowButtonsInterButtonSpacing_;
// fallthrough
default:
break;
}
BOOL didPost = [button postsBoundsChangedNotifications];
[button setPostsFrameChangedNotifications:NO];
[button setFrame:buttonFrame];
[button setPostsFrameChangedNotifications:didPost];
}
- (NSView*)frameView {
return [[self contentView] superview];
}
2016-10-13 00:21:56 +00:00
// Quicklook methods
2016-10-24 18:54:22 +00:00
- (BOOL)acceptsPreviewPanelControl:(QLPreviewPanel*)panel {
2016-10-13 00:21:56 +00:00
return YES;
}
2016-10-24 18:54:22 +00:00
- (void)beginPreviewPanelControl:(QLPreviewPanel*)panel {
2016-10-13 00:21:56 +00:00
panel.delegate = self;
panel.dataSource = self;
}
2016-10-24 18:54:22 +00:00
- (void)endPreviewPanelControl:(QLPreviewPanel*)panel {
2016-10-13 00:21:56 +00:00
panel.delegate = nil;
panel.dataSource = nil;
}
2016-10-24 18:54:22 +00:00
- (NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel*)panel {
2016-10-13 00:21:56 +00:00
return 1;
}
2016-10-24 18:55:48 +00:00
- (id <QLPreviewItem>)previewPanel:(QLPreviewPanel*)panel previewItemAtIndex:(NSInteger)index {
2016-10-13 00:21:56 +00:00
return [self quickLookItem];
}
2016-10-24 18:55:48 +00:00
- (void)previewFileAtPath:(NSString*)path withName:(NSString*) fileName {
2016-10-25 14:52:56 +00:00
NSURL* url = [[[NSURL alloc] initFileURLWithPath:path] autorelease];
2016-10-14 16:42:50 +00:00
[self setQuickLookItem:[[[AtomPreviewItem alloc] initWithURL:url title:fileName] autorelease]];
2016-10-13 00:21:56 +00:00
[[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil];
}
2017-06-05 18:08:05 +00:00
// Custom window button methods
- (void)performClose:(id)sender {
if (shell_->title_bar_style() == atom::NativeWindowMac::CUSTOM_BUTTONS_ON_HOVER)
2017-06-05 19:50:18 +00:00
[[self delegate] windowShouldClose:self];
2017-06-05 20:13:16 +00:00
else
[super performClose:sender];
}
- (void)toggleFullScreenMode:(id)sender {
if (shell_->simple_fullscreen())
shell_->SetSimpleFullScreen(!shell_->IsSimpleFullScreen());
else
[super toggleFullScreen:sender];
}
- (void)performMiniaturize:(id)sender {
if (shell_->title_bar_style() == atom::NativeWindowMac::CUSTOM_BUTTONS_ON_HOVER)
2017-06-05 19:50:18 +00:00
[self miniaturize:self];
2017-06-05 20:13:16 +00:00
else
[super performMiniaturize:sender];
}
@end
@interface ControlRegionView : NSView
@end
@implementation ControlRegionView
- (BOOL)mouseDownCanMoveWindow {
return NO;
}
- (NSView*)hitTest:(NSPoint)aPoint {
return nil;
}
@end
@interface NSView (WebContentsView)
- (void)setMouseDownCanMoveWindow:(BOOL)can_move;
@end
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;
NSBezierPath* bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:radius yRadius:radius];
[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;
bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:radius yRadius:radius];
[bezier_path setLineWidth:1.0];
[bezier_path addClip];
2014-09-17 07:58:08 +00:00
// Calculate the progress width.
2014-09-18 08:17:49 +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 {
template<>
struct Converter<atom::NativeWindowMac::TitleBarStyle> {
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 {
NativeWindowMac::NativeWindowMac(
brightray::InspectableWebContents* web_contents,
2016-06-19 03:06:08 +00:00
const mate::Dictionary& options,
NativeWindow* parent)
: NativeWindow(web_contents, options, parent),
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
browser_view_(nullptr),
2013-04-12 07:04:46 +00:00
is_kiosk_(false),
2017-01-13 23:04:51 +00:00
was_fullscreen_(false),
2016-10-27 18:29:51 +00:00
zoom_to_page_width_(false),
fullscreen_window_title_(false),
attention_request_id_(0),
title_bar_style_(NORMAL),
always_simple_fullscreen_(false),
is_simple_fullscreen_(false) {
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];
2013-04-12 07:04:46 +00:00
NSRect cocoa_bounds = NSMakeRect(
round((NSWidth(main_screen_rect) - width) / 2) ,
round((NSHeight(main_screen_rect) - height) / 2),
2013-04-12 07:04:46 +00:00
width,
height);
bool resizable = true;
options.Get(options::kResizable, &resizable);
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);
options.Get(options::kTitleBarStyle, &title_bar_style_);
2015-09-25 08:21:08 +00:00
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 (title_bar_style_ == CUSTOM_BUTTONS_ON_HOVER &&
2017-06-05 19:50:18 +00:00
base::mac::IsAtLeastOS10_10() &&
(!useStandardWindow || transparent() || !has_frame())) {
styleMask = NSFullSizeContentViewWindowMask;
}
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;
}
if (resizable) {
styleMask |= NSResizableWindowMask;
}
window_.reset([[AtomNSWindow alloc]
initWithContentRect:cocoa_bounds
styleMask:styleMask
backing:NSBackingStoreBuffered
defer:YES]);
[window_ setShell:this];
[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];
[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()) {
2017-02-07 17:32:40 +00:00
if (base::mac::IsAtLeastOS10_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 ([window_ respondsToSelector:@selector(tabbingMode)]) {
[window_ setTabbingMode:NSWindowTabbingModeDisallowed];
}
} else {
if ([window_ respondsToSelector:@selector(tabbingIdentifier)]) {
2017-03-30 20:47:11 +00:00
[window_ setTabbingIdentifier:base::SysUTF8ToNSString(tabbingIdentifier)];
2017-03-27 14:15:17 +00:00
}
}
// We will manage window's lifetime ourselves.
2014-04-11 04:45:48 +00:00
[window_ setReleasedWhenClosed:NO];
// Hide the title bar background
if (title_bar_style_ != NORMAL) {
2017-02-07 17:32:40 +00:00
if (base::mac::IsAtLeastOS10_10()) {
[window_ setTitlebarAppearsTransparent:YES];
}
}
// Hide the title bar.
if (title_bar_style_ == HIDDEN_INSET) {
if (base::mac::IsAtLeastOS10_10()) {
base::scoped_nsobject<NSToolbar> toolbar(
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
[toolbar setShowsBaselineSeparator:NO];
[window_ setToolbar:toolbar];
} else {
[window_ enableWindowButtonsOffset];
[window_ setWindowButtonsOffset:NSMakePoint(12, 10)];
}
}
2016-06-18 13:26:26 +00:00
// On macOS the initial window size doesn't include window frame.
bool use_content_size = false;
options.Get(options::kUseContentSize, &use_content_size);
if (!has_frame() || !use_content_size)
SetSize(gfx::Size(width, height));
2016-10-27 18:29:51 +00:00
options.Get(options::kZoomToPageWidth, &zoom_to_page_width_);
options.Get(options::kFullscreenWindowTitle, &fullscreen_window_title_);
options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_);
// 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];
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
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
2016-01-22 00:31:31 +00:00
addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
2016-01-25 07:02:43 +00:00
handler:^(NSEvent* event) {
if ([[event window] windowNumber] != [window_ windowNumber])
2016-01-22 00:31:31 +00:00
return event;
if (!web_contents)
2016-01-22 00:31:31 +00:00
return event;
if (!began && (([event phase] == NSEventPhaseMayBegin) ||
([event phase] == NSEventPhaseBegan))) {
2016-01-22 00:31:31 +00:00
this->NotifyWindowScrollTouchBegin();
2016-01-25 07:02:43 +00:00
began = YES;
} else if (began && (([event phase] == NSEventPhaseEnded) ||
([event phase] == NSEventPhaseCancelled))) {
2016-01-22 00:31:31 +00:00
this->NotifyWindowScrollTouchEnd();
2016-01-25 07:02:43 +00:00
began = NO;
2016-01-22 00:31:31 +00:00
}
return event;
2016-01-25 07:02:43 +00:00
}];
2013-04-12 07:04:46 +00:00
InstallView();
std::string type;
if (options.Get(options::kVibrancyType, &type)) {
SetVibrancy(type);
}
// 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);
2016-08-01 10:01:21 +00:00
RegisterInputEventObserver(
web_contents->GetWebContents()->GetRenderViewHost());
2013-04-12 07:04:46 +00:00
}
NativeWindowMac::~NativeWindowMac() {
2016-01-25 07:02:43 +00:00
[NSEvent removeMonitor:wheel_event_monitor_];
Observe(nullptr);
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_
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-02-06 14:16:22 +00:00
[window_ beginSheet: window_ completionHandler:^(NSModalResponse returnCode) {
NSLog(@"modal enabled");
return;
}];
} else {
2018-02-06 14:16:22 +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];
cocoa_bounds.origin.y =
2016-07-07 11:02:18 +00:00
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_ contentRectForFrameRect: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
}
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) {
NSString* path_ns = [NSString stringWithUTF8String:path.c_str()];
NSString* name_ns = [NSString stringWithUTF8String:display_name.c_str()];
[window_ previewFileAtPath:path_ns withName:name_ns];
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) {
2016-01-23 11:12:19 +00:00
SetCollectionBehavior(
fullscreenable, NSWindowCollectionBehaviorFullScreenPrimary);
// On EL Capitan this flag is required to hide fullscreen button.
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;
}
2017-01-30 23:32:05 +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 {
*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() {
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;
}
}
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];
// 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];
if ( !fullscreen_window_title() ) {
// Hide the titlebar
SetStyleMask(false, NSTitledWindowMask);
// Resize the window to accomodate the _entire_ screen size
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];
}
[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;
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];
}
[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();
if (!was_fullscreen_) SetFullScreen(true);
} else if (!kiosk && is_kiosk_) {
is_kiosk_ = false;
2017-01-13 23:04:51 +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_;
}
2015-10-23 03:35:33 +00:00
void NativeWindowMac::SetBackgroundColor(const std::string& color_name) {
SkColor color = ParseHexColor(color_name);
2016-09-06 08:24:37 +00:00
base::ScopedCFTypeRef<CGColorRef> cgcolor(
skia::CGColorCreateFromSkColor(color));
[[[window_ contentView] layer] setBackgroundColor:cgcolor];
const auto view = web_contents()->GetRenderWidgetHostView();
if (view)
view->SetBackgroundColor(color);
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];
}
2017-08-17 07:47:12 +00:00
void NativeWindowMac::SetIgnoreMouseEvents(bool ignore, bool) {
[window_ setIgnoresMouseEvents:ignore];
}
void NativeWindowMac::SetContentProtection(bool enable) {
[window_ setSharingType:enable ? NSWindowSharingNone
: NSWindowSharingReadOnly];
}
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
void NativeWindowMac::SetBrowserView(NativeBrowserView* browser_view) {
if (browser_view_) {
[browser_view_->GetInspectableWebContentsView()->GetNativeView()
removeFromSuperview];
browser_view_ = nullptr;
}
if (!browser_view) {
return;
}
browser_view_ = browser_view;
auto* native_view =
browser_view->GetInspectableWebContentsView()->GetNativeView();
[[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 inspectable_web_contents()->GetView()->GetNativeView();
}
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 {
2016-01-07 22:27:53 +00:00
return inspectable_web_contents()->GetView()->GetNativeView();
2016-01-07 20:38:35 +00:00
}
void NativeWindowMac::SetProgressBar(double progress, const NativeWindow::ProgressState state) {
2014-09-17 07:58:08 +00:00
NSDockTile* dock_tile = [NSApp dockTile];
// 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
}
NSProgressIndicator* progress_indicator =
static_cast<NSProgressIndicator*>([[[dock_tile contentView] subviews]
objectAtIndex:0]);
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,
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 ([window_ respondsToSelector:@selector(selectPreviousTab:)]) {
[window_ selectPreviousTab:nil];
}
}
void NativeWindowMac::SelectNextTab() {
if ([window_ respondsToSelector:@selector(selectNextTab:)]) {
[window_ selectNextTab:nil];
}
}
void NativeWindowMac::MergeAllWindows() {
if ([window_ respondsToSelector:@selector(mergeAllWindows:)]) {
[window_ mergeAllWindows:nil];
}
}
void NativeWindowMac::MoveTabToNewWindow() {
if ([window_ respondsToSelector:@selector(moveTabToNewWindow:)]) {
[window_ moveTabToNewWindow:nil];
}
}
void NativeWindowMac::ToggleTabBar() {
if ([window_ respondsToSelector:@selector(toggleTabBar:)]) {
[window_ toggleTabBar:nil];
}
}
void NativeWindowMac::AddTabbedWindow(NativeWindow* window) {
if ([window_ respondsToSelector:@selector(addTabbedWindow:ordered:)]) {
[window_ addTabbedWindow:window->GetNativeWindow() ordered:NSWindowAbove];
}
}
void NativeWindowMac::SetRenderWidgetHostOpaque(bool opaque) {
if (!web_contents()) return;
auto render_view_host = web_contents()->GetRenderViewHost();
if (!render_view_host) return;
content::RenderWidgetHostImpl* impl = content::RenderWidgetHostImpl::FromID(
render_view_host->GetProcess()->GetID(),
render_view_host->GetRoutingID());
if (!impl) return;
impl->SetBackgroundOpaque(opaque);
}
2016-11-07 20:22:41 +00:00
void NativeWindowMac::SetVibrancy(const std::string& type) {
2017-02-18 19:25:03 +00:00
if (!base::mac::IsAtLeastOS10_10()) return;
2016-11-07 20:22:41 +00:00
NSView* vibrant_view = [window_ vibrantView];
if (type.empty()) {
if (background_color_before_vibrancy_) {
[window_ setBackgroundColor:background_color_before_vibrancy_];
[window_ setTitlebarAppearsTransparent:transparency_before_vibrancy_];
}
SetRenderWidgetHostOpaque(!transparent());
if (vibrant_view == nil) return;
[vibrant_view removeFromSuperview];
[window_ setVibrantView:nil];
return;
}
SetRenderWidgetHostOpaque(false);
background_color_before_vibrancy_.reset([window_ backgroundColor]);
transparency_before_vibrancy_ = [window_ titlebarAppearsTransparent];
[window_ setTitlebarAppearsTransparent:YES];
[window_ setBackgroundColor:[NSColor clearColor]];
NSVisualEffectView* effect_view = (NSVisualEffectView*)vibrant_view;
if (effect_view == nil) {
2016-11-11 16:44:11 +00:00
effect_view = [[[NSVisualEffectView alloc]
initWithFrame: [[window_ contentView] bounds]] autorelease];
[window_ setVibrantView:(NSView*)effect_view];
2016-11-07 20:22:41 +00:00
2016-11-11 16:44:26 +00:00
[effect_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[effect_view setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
[effect_view setState:NSVisualEffectStateActive];
[[window_ contentView] addSubview:effect_view
2016-11-07 20:22:41 +00:00
positioned:NSWindowBelow
relativeTo:nil];
}
NSVisualEffectMaterial vibrancyType = NSVisualEffectMaterialLight;
if (type == "appearance-based") {
vibrancyType = NSVisualEffectMaterialAppearanceBased;
} else if (type == "light") {
vibrancyType = NSVisualEffectMaterialLight;
} else if (type == "dark") {
vibrancyType = NSVisualEffectMaterialDark;
} else if (type == "titlebar") {
vibrancyType = NSVisualEffectMaterialTitlebar;
}
2017-02-07 17:32:40 +00:00
if (base::mac::IsAtLeastOS10_11()) {
// TODO(kevinsawicki): Use NSVisualEffectMaterial* constants directly once
// they are available in the minimum SDK version
2016-11-07 20:22:41 +00:00
if (type == "selection") {
// NSVisualEffectMaterialSelection
2017-04-01 20:01:29 +00:00
vibrancyType = static_cast<NSVisualEffectMaterial>(4);
2016-11-07 20:22:41 +00:00
} else if (type == "menu") {
// NSVisualEffectMaterialMenu
2017-04-01 20:01:29 +00:00
vibrancyType = static_cast<NSVisualEffectMaterial>(5);
2016-11-07 20:22:41 +00:00
} else if (type == "popover") {
// NSVisualEffectMaterialPopover
2017-04-01 20:01:29 +00:00
vibrancyType = static_cast<NSVisualEffectMaterial>(6);
2016-11-07 20:22:41 +00:00
} else if (type == "sidebar") {
// NSVisualEffectMaterialSidebar
2017-04-01 20:01:29 +00:00
vibrancyType = static_cast<NSVisualEffectMaterial>(7);
2016-11-07 20:22:41 +00:00
} else if (type == "medium-light") {
// NSVisualEffectMaterialMediumLight
2017-04-01 20:01:29 +00:00
vibrancyType = static_cast<NSVisualEffectMaterial>(8);
2016-11-07 20:22:41 +00:00
} else if (type == "ultra-dark") {
// NSVisualEffectMaterialUltraDark
2017-04-01 20:01:29 +00:00
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) {
2017-03-01 23:26:10 +00:00
[window_ resetTouchBar:items];
}
2017-03-01 00:08:12 +00:00
void NativeWindowMac::RefreshTouchBarItem(const std::string& item_id) {
[window_ refreshTouchBarItem:item_id];
}
2017-03-29 04:11:39 +00:00
void NativeWindowMac::SetEscapeTouchBarItem(const mate::PersistentDictionary& item) {
[window_ setEscapeTouchBarItem:item];
}
void NativeWindowMac::OnInputEvent(const blink::WebInputEvent& event) {
2017-06-16 20:42:33 +00:00
switch (event.GetType()) {
case blink::WebInputEvent::kGestureScrollBegin:
case blink::WebInputEvent::kGestureScrollUpdate:
case blink::WebInputEvent::kGestureScrollEnd:
this->NotifyWindowScrollTouchEdge();
break;
default:
break;
}
}
void NativeWindowMac::RenderViewHostChanged(
content::RenderViewHost* old_host,
content::RenderViewHost* new_host) {
UnregisterInputEventObserver(old_host);
RegisterInputEventObserver(new_host);
}
std::vector<gfx::Rect> NativeWindowMac::CalculateNonDraggableRegions(
const std::vector<DraggableRegion>& regions, int width, int height) {
std::vector<gfx::Rect> result;
if (regions.empty()) {
result.push_back(gfx::Rect(0, 0, width, height));
} else {
2016-05-23 01:59:39 +00:00
std::unique_ptr<SkRegion> draggable(DraggableRegionsToSkRegion(regions));
std::unique_ptr<SkRegion> non_draggable(new SkRegion);
non_draggable->op(0, 0, width, height, SkRegion::kUnion_Op);
non_draggable->op(*draggable, SkRegion::kDifference_Op);
for (SkRegion::Iterator it(*non_draggable); !it.done(); it.next()) {
result.push_back(gfx::SkIRectToRect(it.rect()));
}
}
return result;
}
gfx::Rect NativeWindowMac::ContentBoundsToWindowBounds(
2017-05-23 09:41:59 +00:00
const gfx::Rect& bounds) const {
2016-08-04 19:02:24 +00:00
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;
2016-08-04 19:02:24 +00:00
}
}
gfx::Rect NativeWindowMac::WindowBoundsToContentBounds(
2017-05-23 09:41:59 +00:00
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;
}
}
void NativeWindowMac::UpdateDraggableRegions(
content::RenderFrameHost* rfh,
const std::vector<DraggableRegion>& regions) {
NativeWindow::UpdateDraggableRegions(rfh, regions);
draggable_regions_ = regions;
UpdateDraggableRegionViews(regions);
}
2017-09-14 05:43:22 +00:00
void NativeWindowMac::InternalSetParentWindow(NativeWindow* parent, bool attach) {
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];
}
2013-04-12 07:04:46 +00:00
void NativeWindowMac::InstallView() {
// 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())
[[window_ contentView] setWantsLayer:YES];
2013-04-12 08:41:15 +00:00
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
if (has_frame()) {
2014-04-11 04:45:48 +00:00
[view setFrame:[[window_ contentView] bounds]];
[[window_ contentView] addSubview:view];
} else {
// 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
content_view_.reset([[FullSizeContentView alloc] init]);
[content_view_
setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[content_view_ setFrame:[[[window_ contentView] superview] bounds]];
[window_ setContentView:content_view_];
[view setFrame:[content_view_ bounds]];
[content_view_ addSubview: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];
[content_view_ addSubview:window_button_view];
2017-06-05 19:50:18 +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];
}
2016-06-18 13:26:26 +00:00
// 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];
}
2013-04-12 07:04:46 +00:00
}
void NativeWindowMac::UninstallView() {
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
2013-04-12 07:04:46 +00:00
[view removeFromSuperview];
}
void NativeWindowMac::UpdateDraggableRegionViews(
const std::vector<DraggableRegion>& regions) {
if (has_frame())
return;
// All ControlRegionViews should be added as children of the WebContentsView,
// because WebContentsView will be removed and re-added when entering and
// leaving fullscreen mode.
2015-06-25 06:54:00 +00:00
NSView* webView = web_contents()->GetNativeView();
NSInteger webViewWidth = NSWidth([webView bounds]);
NSInteger webViewHeight = NSHeight([webView bounds]);
if ([webView respondsToSelector:@selector(setMouseDownCanMoveWindow:)]) {
[webView setMouseDownCanMoveWindow:YES];
}
// Remove all ControlRegionViews that are added last time.
// Note that [webView subviews] returns the view's mutable internal array and
// it should be copied to avoid mutating the original array while enumerating
// it.
base::scoped_nsobject<NSArray> subviews([[webView subviews] copy]);
for (NSView* subview in subviews.get())
if ([subview isKindOfClass:[ControlRegionView class]])
[subview removeFromSuperview];
// Draggable regions is implemented by having the whole web view draggable
// (mouseDownCanMoveWindow) and overlaying regions that are not draggable.
std::vector<gfx::Rect> system_drag_exclude_areas =
CalculateNonDraggableRegions(regions, webViewWidth, webViewHeight);
2017-08-09 21:49:11 +00:00
if (browser_view_) {
browser_view_->UpdateDraggableRegions(system_drag_exclude_areas);
}
// Create and add a ControlRegionView for each region that needs to be
// excluded from the dragging.
for (std::vector<gfx::Rect>::const_iterator iter =
system_drag_exclude_areas.begin();
iter != system_drag_exclude_areas.end();
++iter) {
base::scoped_nsobject<NSView> controlRegion(
[[ControlRegionView alloc] initWithFrame:NSZeroRect]);
[controlRegion setFrame:NSMakeRect(iter->x(),
webViewHeight - iter->bottom(),
iter->width(),
iter->height())];
[webView addSubview:controlRegion];
}
// AppKit will not update its cache of mouseDownCanMoveWindow unless something
// changes. Previously we tried adding an NSView and removing it, but for some
// reason it required reposting the mouse-down event, and didn't always work.
// Calling the below seems to be an effective solution.
[window_ setMovableByWindowBackground:NO];
[window_ setMovableByWindowBackground:YES];
}
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
}
void NativeWindowMac::RegisterInputEventObserver(
content::RenderViewHost* host) {
if (host)
host->GetWidget()->AddInputEventObserver(this);
}
void NativeWindowMac::UnregisterInputEventObserver(
content::RenderViewHost* host) {
if (host)
host->GetWidget()->RemoveInputEventObserver(this);
2016-08-01 10:01:21 +00:00
}
2013-04-12 07:04:46 +00:00
// static
NativeWindow* NativeWindow::Create(
brightray::InspectableWebContents* inspectable_web_contents,
2016-06-19 03:06:08 +00:00
const mate::Dictionary& options,
NativeWindow* parent) {
return new NativeWindowMac(inspectable_web_contents, options, parent);
2013-04-12 07:04:46 +00:00
}
} // namespace atom