fix: clean up implementations of titleBarStyle (#27489)

* Rewrite titleBarStyle impls with WindowButtonsView

* Remove fullscreenWindowTitle option

* Make buttons show correctly under RTL

* Fix docs about traffic lights position

* Fix test on fullscreen resizable

* Fix button states with closabe/minimizable/fullscreenable

* Fix typo

* Deprecate the fullscreenWindowTitle option
This commit is contained in:
Cheng Zhao 2021-01-31 08:15:10 +09:00 committed by GitHub
parent 6edf6c6a95
commit 8bf66f8974
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 262 additions and 304 deletions

View file

@ -222,16 +222,14 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
the top left. the top left.
* `hiddenInset` - Results in a hidden title bar with an alternative look * `hiddenInset` - Results in a hidden title bar with an alternative look
where the traffic light buttons are slightly more inset from the window edge. where the traffic light buttons are slightly more inset from the window edge.
* `customButtonsOnHover` Boolean (optional) - Draw custom close, * `customButtonsOnHover` - Results in a hidden title bar and a full size
and minimize buttons on macOS frameless windows. These buttons will not display content window, the traffic light buttons will display when being hovered
unless hovered over in the top left of the window. These custom buttons prevent over in the top left of the window. **Note:** This option is currently
issues with mouse events that occur with the standard window toolbar buttons. experimental.
**Note:** This option is currently experimental.
* `trafficLightPosition` [Point](structures/point.md) (optional) - Set a * `trafficLightPosition` [Point](structures/point.md) (optional) - Set a
custom position for the traffic light buttons. Can only be used with custom position for the traffic light buttons in frameless windows.
`titleBarStyle` set to `hidden` or `customButtonsOnHover`. * `fullscreenWindowTitle` Boolean (optional) _Deprecated_ - Shows the title in
* `fullscreenWindowTitle` Boolean (optional) - Shows the title in the the title bar in full screen mode on macOS for `hiddenInset` titleBarStyle.
title bar in full screen mode on macOS for all `titleBarStyle` options.
Default is `false`. Default is `false`.
* `thickFrame` Boolean (optional) - Use `WS_THICKFRAME` style for frameless windows on * `thickFrame` Boolean (optional) - Use `WS_THICKFRAME` style for frameless windows on
Windows, which adds standard window frame. Setting it to `false` will remove Windows, which adds standard window frame. Setting it to `false` will remove
@ -1740,13 +1738,12 @@ deprecated and will be removed in an upcoming version of macOS.
* `position` [Point](structures/point.md) * `position` [Point](structures/point.md)
Set a custom position for the traffic light buttons. Can only be used with Set a custom position for the traffic light buttons in frameless window.
`titleBarStyle` set to `hidden` or `customButtonsOnHover`.
#### `win.getTrafficLightPosition()` _macOS_ #### `win.getTrafficLightPosition()` _macOS_
Returns `Point` - The current position for the traffic light buttons. Can only Returns `Point` - The custom position for the traffic light buttons in
be used with `titleBarStyle` set to `hidden` or `customButtonsOnHover`. frameless window.
#### `win.setTouchBar(touchBar)` _macOS_ #### `win.setTouchBar(touchBar)` _macOS_

View file

@ -180,6 +180,8 @@ filenames = {
"shell/browser/ui/cocoa/root_view_mac.mm", "shell/browser/ui/cocoa/root_view_mac.mm",
"shell/browser/ui/cocoa/views_delegate_mac.h", "shell/browser/ui/cocoa/views_delegate_mac.h",
"shell/browser/ui/cocoa/views_delegate_mac.mm", "shell/browser/ui/cocoa/views_delegate_mac.mm",
"shell/browser/ui/cocoa/window_buttons_view.h",
"shell/browser/ui/cocoa/window_buttons_view.mm",
"shell/browser/ui/drag_util_mac.mm", "shell/browser/ui/drag_util_mac.mm",
"shell/browser/ui/file_dialog_mac.mm", "shell/browser/ui/file_dialog_mac.mm",
"shell/browser/ui/inspectable_web_contents_view_mac.h", "shell/browser/ui/inspectable_web_contents_view_mac.h",

View file

@ -280,8 +280,8 @@ class NativeWindow : public base::SupportsUserData,
void NotifyWindowRotateGesture(float rotation); void NotifyWindowRotateGesture(float rotation);
void NotifyWindowSheetBegin(); void NotifyWindowSheetBegin();
void NotifyWindowSheetEnd(); void NotifyWindowSheetEnd();
void NotifyWindowEnterFullScreen(); virtual void NotifyWindowEnterFullScreen();
void NotifyWindowLeaveFullScreen(); virtual void NotifyWindowLeaveFullScreen();
void NotifyWindowEnterHtmlFullScreen(); void NotifyWindowEnterHtmlFullScreen();
void NotifyWindowLeaveHtmlFullScreen(); void NotifyWindowLeaveHtmlFullScreen();
void NotifyWindowAlwaysOnTopChanged(); void NotifyWindowAlwaysOnTopChanged();

View file

@ -21,7 +21,7 @@
@class ElectronNSWindowDelegate; @class ElectronNSWindowDelegate;
@class ElectronPreviewItem; @class ElectronPreviewItem;
@class ElectronTouchBar; @class ElectronTouchBar;
@class CustomWindowButtonView; @class WindowButtonsView;
namespace electron { namespace electron {
@ -139,6 +139,11 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
void CloseFilePreview() override; void CloseFilePreview() override;
gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) const override; gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) const override;
gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) const override; gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) const override;
void NotifyWindowEnterFullScreen() override;
void NotifyWindowLeaveFullScreen() override;
void NotifyWindowWillEnterFullScreen();
void NotifyWindowWillLeaveFullScreen();
// Cleanup observers when window is getting closed. Note that the destructor // Cleanup observers when window is getting closed. Note that the destructor
// can be called much later after window gets closed, so we should not do // can be called much later after window gets closed, so we should not do
@ -153,8 +158,6 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
void SetCollectionBehavior(bool on, NSUInteger flag); void SetCollectionBehavior(bool on, NSUInteger flag);
void SetWindowLevel(int level); void SetWindowLevel(int level);
void SetExitingFullScreen(bool flag);
enum class VisualEffectState { enum class VisualEffectState {
kFollowWindow, kFollowWindow,
kActive, kActive,
@ -172,7 +175,6 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
ElectronPreviewItem* preview_item() const { return preview_item_.get(); } ElectronPreviewItem* preview_item() const { return preview_item_.get(); }
ElectronTouchBar* touch_bar() const { return touch_bar_.get(); } ElectronTouchBar* touch_bar() const { return touch_bar_.get(); }
bool zoom_to_page_width() const { return zoom_to_page_width_; } bool zoom_to_page_width() const { return zoom_to_page_width_; }
bool fullscreen_window_title() const { return fullscreen_window_title_; }
bool always_simple_fullscreen() const { return always_simple_fullscreen_; } bool always_simple_fullscreen() const { return always_simple_fullscreen_; }
bool exiting_fullscreen() const { return exiting_fullscreen_; } bool exiting_fullscreen() const { return exiting_fullscreen_; }
@ -186,9 +188,10 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
private: private:
// Add custom layers to the content view. // Add custom layers to the content view.
void AddContentViewLayers(bool minimizable, bool closable); void AddContentViewLayers();
void InternalSetWindowButtonVisibility(bool visible); void InternalSetWindowButtonVisibility(bool visible);
void InternalSetStandardButtonsVisibility(bool visible);
void InternalSetParentWindow(NativeWindow* parent, bool attach); void InternalSetParentWindow(NativeWindow* parent, bool attach);
void SetForwardMouseMessages(bool forward); void SetForwardMouseMessages(bool forward);
@ -197,7 +200,7 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
base::scoped_nsobject<ElectronNSWindowDelegate> window_delegate_; base::scoped_nsobject<ElectronNSWindowDelegate> window_delegate_;
base::scoped_nsobject<ElectronPreviewItem> preview_item_; base::scoped_nsobject<ElectronPreviewItem> preview_item_;
base::scoped_nsobject<ElectronTouchBar> touch_bar_; base::scoped_nsobject<ElectronTouchBar> touch_bar_;
base::scoped_nsobject<CustomWindowButtonView> buttons_view_; base::scoped_nsobject<WindowButtonsView> buttons_view_;
// Event monitor for scroll wheel event. // Event monitor for scroll wheel event.
id wheel_event_monitor_; id wheel_event_monitor_;
@ -213,7 +216,6 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
bool is_kiosk_ = false; bool is_kiosk_ = false;
bool was_fullscreen_ = false; bool was_fullscreen_ = false;
bool zoom_to_page_width_ = false; bool zoom_to_page_width_ = false;
bool fullscreen_window_title_ = false;
bool resizable_ = true; bool resizable_ = true;
bool exiting_fullscreen_ = false; bool exiting_fullscreen_ = false;
base::Optional<gfx::Point> traffic_light_position_; base::Optional<gfx::Point> traffic_light_position_;

View file

@ -29,6 +29,7 @@
#include "shell/browser/ui/cocoa/electron_preview_item.h" #include "shell/browser/ui/cocoa/electron_preview_item.h"
#include "shell/browser/ui/cocoa/electron_touch_bar.h" #include "shell/browser/ui/cocoa/electron_touch_bar.h"
#include "shell/browser/ui/cocoa/root_view_mac.h" #include "shell/browser/ui/cocoa/root_view_mac.h"
#include "shell/browser/ui/cocoa/window_buttons_view.h"
#include "shell/browser/ui/inspectable_web_contents.h" #include "shell/browser/ui/inspectable_web_contents.h"
#include "shell/browser/ui/inspectable_web_contents_view.h" #include "shell/browser/ui/inspectable_web_contents_view.h"
#include "shell/browser/window_list.h" #include "shell/browser/window_list.h"
@ -119,106 +120,6 @@
@end @end
// Custom Quit, Minimize and Full Screen button container for frameless
// windows.
@interface CustomWindowButtonView : NSView {
@private
BOOL mouse_inside_;
gfx::Point margin_;
}
- (id)initWithMargin:(const base::Optional<gfx::Point>&)margin;
- (void)setMargin:(const base::Optional<gfx::Point>&)margin;
@end
@implementation CustomWindowButtonView
- (id)initWithMargin:(const base::Optional<gfx::Point>&)margin {
self = [super initWithFrame:NSZeroRect];
[self setMargin:margin];
NSButton* close_button =
[NSWindow standardWindowButton:NSWindowCloseButton
forStyleMask:NSWindowStyleMaskTitled];
[close_button setTag:1];
NSButton* miniaturize_button =
[NSWindow standardWindowButton:NSWindowMiniaturizeButton
forStyleMask:NSWindowStyleMaskTitled];
[miniaturize_button setTag:2];
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];
const auto last_button_frame = miniaturize_button.frame;
[self setFrameSize:NSMakeSize(last_button_frame.origin.x +
last_button_frame.size.width,
last_button_frame.size.height)];
mouse_inside_ = NO;
[self setNeedsDisplayForButtons];
return self;
}
- (void)setMargin:(const base::Optional<gfx::Point>&)margin {
margin_ = margin.value_or(gfx::Point(7, 3));
}
- (void)viewDidMoveToWindow {
if (!self.window) {
return;
}
// Stay in upper left corner.
[self setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin];
[self setFrameOrigin:NSMakePoint(margin_.x(), self.window.frame.size.height -
self.frame.size.height -
margin_.y())];
}
- (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];
}
- (void)mouseEntered:(NSEvent*)event {
[super mouseEntered:event];
mouse_inside_ = YES;
[self setNeedsDisplayForButtons];
}
- (void)mouseExited:(NSEvent*)event {
[super mouseExited:event];
mouse_inside_ = NO;
[self setNeedsDisplayForButtons];
}
- (void)setNeedsDisplayForButtons {
for (NSView* subview in self.subviews) {
[subview setHidden:!mouse_inside_];
[subview setNeedsDisplay:YES];
}
}
@end
@interface ElectronProgressBar : NSProgressIndicator @interface ElectronProgressBar : NSProgressIndicator
@end @end
@ -370,11 +271,17 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
options.Get(options::kResizable, &resizable_); options.Get(options::kResizable, &resizable_);
options.Get(options::kTitleBarStyle, &title_bar_style_); options.Get(options::kTitleBarStyle, &title_bar_style_);
options.Get(options::kZoomToPageWidth, &zoom_to_page_width_); options.Get(options::kZoomToPageWidth, &zoom_to_page_width_);
options.Get(options::kFullscreenWindowTitle, &fullscreen_window_title_);
options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_); options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_);
options.GetOptional(options::kTrafficLightPosition, &traffic_light_position_); options.GetOptional(options::kTrafficLightPosition, &traffic_light_position_);
options.Get(options::kVisualEffectState, &visual_effect_state_); options.Get(options::kVisualEffectState, &visual_effect_state_);
if (options.Has(options::kFullscreenWindowTitle)) {
EmitWarning(node::Environment::GetCurrent(v8::Isolate::GetCurrent()),
"\"fullscreenWindowTitle\" option has been deprecated and is "
"no-op now.",
"electron");
}
bool minimizable = true; bool minimizable = true;
options.Get(options::kMinimizable, &minimizable); options.Get(options::kMinimizable, &minimizable);
@ -457,6 +364,8 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
[window_ setTitleVisibility:NSWindowTitleHidden]; [window_ setTitleVisibility:NSWindowTitleHidden];
// Remove non-transparent corners, see http://git.io/vfonD. // Remove non-transparent corners, see http://git.io/vfonD.
[window_ setOpaque:NO]; [window_ setOpaque:NO];
// Hide the window buttons.
InternalSetStandardButtonsVisibility(false);
} }
// Create a tab only if tabbing identifier is specified and window has // Create a tab only if tabbing identifier is specified and window has
@ -471,14 +380,6 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
} }
} }
// Hide the title bar.
if (title_bar_style_ == TitleBarStyle::kHiddenInset) {
base::scoped_nsobject<NSToolbar> toolbar(
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
[toolbar setShowsBaselineSeparator:NO];
[window_ setToolbar:toolbar];
}
// Resize to content bounds. // Resize to content bounds.
bool use_content_size = false; bool use_content_size = false;
options.Get(options::kUseContentSize, &use_content_size); options.Get(options::kUseContentSize, &use_content_size);
@ -527,7 +428,7 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
// Default content view. // Default content view.
SetContentView(new views::View()); SetContentView(new views::View());
AddContentViewLayers(minimizable, closable); AddContentViewLayers();
original_frame_ = [window_ frame]; original_frame_ = [window_ frame];
original_level_ = [window_ level]; original_level_ = [window_ level];
@ -830,6 +731,8 @@ bool NativeWindowMac::IsMovable() {
void NativeWindowMac::SetMinimizable(bool minimizable) { void NativeWindowMac::SetMinimizable(bool minimizable) {
SetStyleMask(minimizable, NSMiniaturizableWindowMask); SetStyleMask(minimizable, NSMiniaturizableWindowMask);
if (buttons_view_)
[[buttons_view_ viewWithTag:1] setEnabled:minimizable];
} }
bool NativeWindowMac::IsMinimizable() { bool NativeWindowMac::IsMinimizable() {
@ -851,6 +754,8 @@ void NativeWindowMac::SetFullScreenable(bool fullscreenable) {
// On EL Capitan this flag is required to hide fullscreen button. // On EL Capitan this flag is required to hide fullscreen button.
SetCollectionBehavior(!fullscreenable, SetCollectionBehavior(!fullscreenable,
NSWindowCollectionBehaviorFullScreenAuxiliary); NSWindowCollectionBehaviorFullScreenAuxiliary);
if (buttons_view_)
[[buttons_view_ viewWithTag:2] setEnabled:fullscreenable];
} }
bool NativeWindowMac::IsFullScreenable() { bool NativeWindowMac::IsFullScreenable() {
@ -860,6 +765,8 @@ bool NativeWindowMac::IsFullScreenable() {
void NativeWindowMac::SetClosable(bool closable) { void NativeWindowMac::SetClosable(bool closable) {
SetStyleMask(closable, NSWindowStyleMaskClosable); SetStyleMask(closable, NSWindowStyleMaskClosable);
if (buttons_view_)
[[buttons_view_ viewWithTag:0] setEnabled:closable];
} }
bool NativeWindowMac::IsClosable() { bool NativeWindowMac::IsClosable() {
@ -940,9 +847,6 @@ void NativeWindowMac::Invalidate() {
void NativeWindowMac::SetTitle(const std::string& title) { void NativeWindowMac::SetTitle(const std::string& title) {
[window_ setTitle:base::SysUTF8ToNSString(title)]; [window_ setTitle:base::SysUTF8ToNSString(title)];
if (title_bar_style_ == TitleBarStyle::kHidden) {
RedrawTrafficLights();
}
} }
std::string NativeWindowMac::GetTitle() { std::string NativeWindowMac::GetTitle() {
@ -1004,14 +908,7 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
window.level = NSPopUpMenuWindowLevel; window.level = NSPopUpMenuWindowLevel;
} }
if (!fullscreen_window_title()) { if (!window_button_visibility_.has_value()) {
// Hide the titlebar
SetStyleMask(false, NSWindowStyleMaskTitled);
// Resize the window to accommodate the _entire_ screen size
fullscreenFrame.size.height -=
[[[NSApplication sharedApplication] mainMenu] menuBarHeight];
} else if (!window_button_visibility_.has_value()) {
// Lets keep previous behaviour - hide window controls in titled // Lets keep previous behaviour - hide window controls in titled
// fullscreen mode when not specified otherwise. // fullscreen mode when not specified otherwise.
InternalSetWindowButtonVisibility(false); InternalSetWindowButtonVisibility(false);
@ -1027,11 +924,6 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
} else if (!simple_fullscreen && is_simple_fullscreen_) { } else if (!simple_fullscreen && is_simple_fullscreen_) {
is_simple_fullscreen_ = false; is_simple_fullscreen_ = false;
if (!fullscreen_window_title()) {
// Restore the titlebar
SetStyleMask(true, NSWindowStyleMaskTitled);
}
// Restore default window controls visibility state. // Restore default window controls visibility state.
if (!window_button_visibility_.has_value()) { if (!window_button_visibility_.has_value()) {
bool visibility; bool visibility;
@ -1450,9 +1342,7 @@ bool NativeWindowMac::GetWindowButtonVisibility() const {
void NativeWindowMac::SetTrafficLightPosition( void NativeWindowMac::SetTrafficLightPosition(
base::Optional<gfx::Point> position) { base::Optional<gfx::Point> position) {
traffic_light_position_ = std::move(position); traffic_light_position_ = std::move(position);
if (title_bar_style_ == TitleBarStyle::kHidden) { if (buttons_view_) {
RedrawTrafficLights();
} else if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover) {
[buttons_view_ setMargin:position]; [buttons_view_ setMargin:position];
[buttons_view_ viewDidMoveToWindow]; [buttons_view_ viewDidMoveToWindow];
} }
@ -1463,64 +1353,8 @@ base::Optional<gfx::Point> NativeWindowMac::GetTrafficLightPosition() const {
} }
void NativeWindowMac::RedrawTrafficLights() { void NativeWindowMac::RedrawTrafficLights() {
// Ensure maximizable options retain pre-existing state. if (buttons_view_)
SetMaximizable(maximizable_); [buttons_view_ setNeedsDisplayForButtons];
// Changing system titlebar is only allowed for "hidden" titleBarStyle.
if (!traffic_light_position_ || title_bar_style_ != TitleBarStyle::kHidden)
return;
if (IsFullscreen())
return;
NSWindow* window = window_;
NSButton* close = [window standardWindowButton:NSWindowCloseButton];
NSButton* miniaturize =
[window standardWindowButton:NSWindowMiniaturizeButton];
NSButton* zoom = [window standardWindowButton:NSWindowZoomButton];
// Safety check just in case apple changes the view structure in a macOS
// update
DCHECK(close.superview);
DCHECK(close.superview.superview);
if (!close.superview || !close.superview.superview)
return;
NSView* titleBarContainerView = close.superview.superview;
// Hide the container when exiting fullscreen, otherwise traffic light buttons
// jump
if (exiting_fullscreen_) {
[titleBarContainerView setHidden:YES];
return;
}
[titleBarContainerView setHidden:NO];
CGFloat buttonHeight = [close frame].size.height;
CGFloat titleBarFrameHeight = buttonHeight + traffic_light_position_->y();
CGRect titleBarRect = titleBarContainerView.frame;
CGFloat titleBarWidth = NSWidth(titleBarRect);
titleBarRect.size.height = titleBarFrameHeight;
titleBarRect.origin.y = window.frame.size.height - titleBarFrameHeight;
[titleBarContainerView setFrame:titleBarRect];
BOOL isRTL = [titleBarContainerView userInterfaceLayoutDirection] ==
NSUserInterfaceLayoutDirectionRightToLeft;
NSArray* windowButtons = @[ close, miniaturize, zoom ];
const CGFloat space_between =
[miniaturize frame].origin.x - [close frame].origin.x;
for (NSUInteger i = 0; i < windowButtons.count; i++) {
NSView* view = [windowButtons objectAtIndex:i];
CGRect rect = [view frame];
if (isRTL) {
CGFloat buttonWidth = NSWidth(rect);
// origin is always top-left, even in RTL
rect.origin.x = titleBarWidth - traffic_light_position_->x() +
(i * space_between) - buttonWidth;
} else {
rect.origin.x = traffic_light_position_->x() + (i * space_between);
}
rect.origin.y = (titleBarFrameHeight - rect.size.height) / 2;
[view setFrameOrigin:rect.origin];
}
} }
void NativeWindowMac::SetTouchBar( void NativeWindowMac::SetTouchBar(
@ -1642,6 +1476,41 @@ gfx::Rect NativeWindowMac::WindowBoundsToContentBounds(
} }
} }
void NativeWindowMac::NotifyWindowEnterFullScreen() {
NativeWindow::NotifyWindowEnterFullScreen();
// Restore the window title under fullscreen mode.
if (buttons_view_)
[window_ setTitleVisibility:NSWindowTitleVisible];
RedrawTrafficLights();
}
void NativeWindowMac::NotifyWindowLeaveFullScreen() {
NativeWindow::NotifyWindowLeaveFullScreen();
exiting_fullscreen_ = false;
// Add back buttonsView after leaving fullscreen mode.
if (buttons_view_) {
InternalSetStandardButtonsVisibility(false);
[[window_ contentView] addSubview:buttons_view_];
}
}
void NativeWindowMac::NotifyWindowWillEnterFullScreen() {
// Remove the buttonsView otherwise window buttons won't show under
// fullscreen mode.
if (buttons_view_) {
[buttons_view_ removeFromSuperview];
InternalSetStandardButtonsVisibility(true);
}
}
void NativeWindowMac::NotifyWindowWillLeaveFullScreen() {
// Hide window title after leaving fullscreen.
if (buttons_view_)
[window_ setTitleVisibility:NSWindowTitleHidden];
exiting_fullscreen_ = true;
RedrawTrafficLights();
}
void NativeWindowMac::Cleanup() { void NativeWindowMac::Cleanup() {
DCHECK(!IsClosed()); DCHECK(!IsClosed());
ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this); ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this);
@ -1663,7 +1532,7 @@ void NativeWindowMac::OverrideNSWindowContentView() {
[container_view_ [container_view_
setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[window_ setContentView:container_view_]; [window_ setContentView:container_view_];
AddContentViewLayers(IsMinimizable(), IsClosable()); AddContentViewLayers();
} }
void NativeWindowMac::SetStyleMask(bool on, NSUInteger flag) { void NativeWindowMac::SetStyleMask(bool on, NSUInteger flag) {
@ -1692,10 +1561,6 @@ void NativeWindowMac::SetCollectionBehavior(bool on, NSUInteger flag) {
SetMaximizable(was_maximizable); SetMaximizable(was_maximizable);
} }
void NativeWindowMac::SetExitingFullScreen(bool flag) {
exiting_fullscreen_ = flag;
}
bool NativeWindowMac::CanResize() const { bool NativeWindowMac::CanResize() const {
return resizable_; return resizable_;
} }
@ -1710,7 +1575,7 @@ void NativeWindowMac::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) {
base::BindOnce(&NativeWindow::RedrawTrafficLights, GetWeakPtr())); base::BindOnce(&NativeWindow::RedrawTrafficLights, GetWeakPtr()));
} }
void NativeWindowMac::AddContentViewLayers(bool minimizable, bool closable) { void NativeWindowMac::AddContentViewLayers() {
// Make sure the bottom corner is rounded for non-modal windows: // Make sure the bottom corner is rounded for non-modal windows:
// http://crbug.com/396264. // http://crbug.com/396264.
if (!is_modal()) { if (!is_modal()) {
@ -1742,46 +1607,39 @@ void NativeWindowMac::AddContentViewLayers(bool minimizable, bool closable) {
[[window_ contentView] viewDidMoveToWindow]; [[window_ contentView] viewDidMoveToWindow];
} }
// The fullscreen button should always be hidden for frameless window. // Create a custom window buttons view.
[[window_ standardWindowButton:NSWindowFullScreenButton] setHidden:YES]; if (title_bar_style_ != TitleBarStyle::kNormal) {
buttons_view_.reset(
[[WindowButtonsView alloc] initWithMargin:traffic_light_position_]);
if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover)
[buttons_view_ setShowOnHover:YES];
if (title_bar_style_ == TitleBarStyle::kHiddenInset &&
!traffic_light_position_)
[buttons_view_ setMargin:gfx::Point(12, 11)];
// Create a custom window buttons view for kCustomButtonsOnHover. if (!IsClosable())
if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover) { [[buttons_view_ viewWithTag:0] setEnabled:NO];
buttons_view_.reset([[CustomWindowButtonView alloc] if (!IsMinimizable())
initWithMargin:traffic_light_position_]); [[buttons_view_ viewWithTag:1] setEnabled:NO];
if (!IsFullScreenable())
if (!minimizable) [[buttons_view_ viewWithTag:2] setEnabled:NO];
[[buttons_view_ viewWithTag:2] removeFromSuperview];
if (!closable)
[[buttons_view_ viewWithTag:1] removeFromSuperview];
[[window_ contentView] addSubview:buttons_view_]; [[window_ contentView] addSubview:buttons_view_];
} }
// Hide the window buttons except for kHidden and kHiddenInset.
if (title_bar_style_ == TitleBarStyle::kNormal ||
title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover) {
[[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.
SetMaximizable(false);
}
} }
} }
void NativeWindowMac::InternalSetWindowButtonVisibility(bool visible) { void NativeWindowMac::InternalSetWindowButtonVisibility(bool visible) {
if (buttons_view_) { if (buttons_view_)
[buttons_view_ setHidden:!visible]; [buttons_view_ setHidden:!visible];
} else { else
[[window_ standardWindowButton:NSWindowCloseButton] setHidden:!visible]; InternalSetStandardButtonsVisibility(visible);
[[window_ standardWindowButton:NSWindowMiniaturizeButton]
setHidden:!visible];
[[window_ standardWindowButton:NSWindowZoomButton] setHidden:!visible];
} }
void NativeWindowMac::InternalSetStandardButtonsVisibility(bool visible) {
[[window_ standardWindowButton:NSWindowCloseButton] setHidden:!visible];
[[window_ standardWindowButton:NSWindowMiniaturizeButton] setHidden:!visible];
[[window_ standardWindowButton:NSWindowZoomButton] setHidden:!visible];
} }
void NativeWindowMac::InternalSetParentWindow(NativeWindow* parent, void NativeWindowMac::InternalSetParentWindow(NativeWindow* parent,

View file

@ -91,14 +91,17 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
- (void)windowDidBecomeMain:(NSNotification*)notification { - (void)windowDidBecomeMain:(NSNotification*)notification {
shell_->NotifyWindowFocus(); shell_->NotifyWindowFocus();
shell_->RedrawTrafficLights();
} }
- (void)windowDidResignMain:(NSNotification*)notification { - (void)windowDidResignMain:(NSNotification*)notification {
shell_->NotifyWindowBlur(); shell_->NotifyWindowBlur();
shell_->RedrawTrafficLights();
} }
- (void)windowDidBecomeKey:(NSNotification*)notification { - (void)windowDidBecomeKey:(NSNotification*)notification {
shell_->NotifyWindowIsKeyChanged(true); shell_->NotifyWindowIsKeyChanged(true);
shell_->RedrawTrafficLights();
} }
- (void)windowDidResignKey:(NSNotification*)notification { - (void)windowDidResignKey:(NSNotification*)notification {
@ -110,6 +113,7 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
return; return;
shell_->NotifyWindowIsKeyChanged(false); shell_->NotifyWindowIsKeyChanged(false);
shell_->RedrawTrafficLights();
} }
- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize { - (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize {
@ -151,9 +155,6 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
- (void)windowDidResize:(NSNotification*)notification { - (void)windowDidResize:(NSNotification*)notification {
[super windowDidResize:notification]; [super windowDidResize:notification];
shell_->NotifyWindowResize(); shell_->NotifyWindowResize();
if (shell_->title_bar_style() == TitleBarStyle::kHidden) {
shell_->RedrawTrafficLights();
}
} }
- (void)windowWillMove:(NSNotification*)notification { - (void)windowWillMove:(NSNotification*)notification {
@ -212,75 +213,23 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
} }
- (void)windowWillEnterFullScreen:(NSNotification*)notification { - (void)windowWillEnterFullScreen:(NSNotification*)notification {
// Setting resizable to true before entering fullscreen shell_->NotifyWindowWillEnterFullScreen();
// Setting resizable to true before entering fullscreen.
is_resizable_ = shell_->IsResizable(); is_resizable_ = shell_->IsResizable();
shell_->SetResizable(true); shell_->SetResizable(true);
// Hide the native toolbar before entering fullscreen, so there is no visual
// artifacts.
if (shell_->title_bar_style() == TitleBarStyle::kHiddenInset) {
NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow();
[window setToolbar:nil];
}
} }
- (void)windowDidEnterFullScreen:(NSNotification*)notification { - (void)windowDidEnterFullScreen:(NSNotification*)notification {
shell_->NotifyWindowEnterFullScreen(); 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().GetNativeNSWindow();
if ((shell_->transparent() || !shell_->has_frame()) &&
// 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() != TitleBarStyle::kHiddenInset ||
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.
if (shell_->title_bar_style() == TitleBarStyle::kHiddenInset) {
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.
[window setTitlebarAppearsTransparent:NO];
shell_->SetStyleMask(true, NSWindowStyleMaskFullSizeContentView);
}
} }
- (void)windowWillExitFullScreen:(NSNotification*)notification { - (void)windowWillExitFullScreen:(NSNotification*)notification {
// Restore the titlebar visibility. shell_->NotifyWindowWillLeaveFullScreen();
NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow();
if ((shell_->transparent() || !shell_->has_frame()) &&
(shell_->title_bar_style() != TitleBarStyle::kHiddenInset ||
shell_->fullscreen_window_title())) {
[window setTitleVisibility:NSWindowTitleHidden];
}
// Turn off the style for toolbar.
if (shell_->title_bar_style() == TitleBarStyle::kHiddenInset) {
shell_->SetStyleMask(false, NSWindowStyleMaskFullSizeContentView);
[window setTitlebarAppearsTransparent:YES];
}
shell_->SetExitingFullScreen(true);
if (shell_->title_bar_style() == TitleBarStyle::kHidden) {
shell_->RedrawTrafficLights();
}
} }
- (void)windowDidExitFullScreen:(NSNotification*)notification { - (void)windowDidExitFullScreen:(NSNotification*)notification {
shell_->SetResizable(is_resizable_); shell_->SetResizable(is_resizable_);
shell_->NotifyWindowLeaveFullScreen(); shell_->NotifyWindowLeaveFullScreen();
shell_->SetExitingFullScreen(false);
if (shell_->title_bar_style() == TitleBarStyle::kHidden) {
shell_->RedrawTrafficLights();
}
} }
- (void)windowWillClose:(NSNotification*)notification { - (void)windowWillClose:(NSNotification*)notification {

View file

@ -0,0 +1,31 @@
// Copyright (c) 2021 Microsoft, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_VIEW_H_
#define SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_VIEW_H_
#import <Cocoa/Cocoa.h>
#include "base/mac/scoped_nsobject.h"
#include "base/optional.h"
#include "ui/gfx/geometry/point.h"
// Custom Quit, Minimize and Full Screen button container for frameless
// windows.
@interface WindowButtonsView : NSView {
@private
BOOL mouse_inside_;
BOOL show_on_hover_;
BOOL is_rtl_;
gfx::Point margin_;
base::scoped_nsobject<NSTrackingArea> tracking_area_;
}
- (id)initWithMargin:(const base::Optional<gfx::Point>&)margin;
- (void)setMargin:(const base::Optional<gfx::Point>&)margin;
- (void)setShowOnHover:(BOOL)yes;
- (void)setNeedsDisplayForButtons;
@end
#endif // SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_VIEW_H_

View file

@ -0,0 +1,119 @@
// Copyright (c) 2021 Microsoft, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/ui/cocoa/window_buttons_view.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "ui/gfx/mac/coordinate_conversion.h"
namespace {
const CGFloat kButtonPadding = 20.;
const NSWindowButton kButtonTypes[] = {
NSWindowCloseButton,
NSWindowMiniaturizeButton,
NSWindowZoomButton,
};
} // namespace
@implementation WindowButtonsView
- (id)initWithMargin:(const base::Optional<gfx::Point>&)margin {
self = [super initWithFrame:NSZeroRect];
[self setMargin:margin];
mouse_inside_ = false;
show_on_hover_ = false;
is_rtl_ = base::i18n::IsRTL();
for (size_t i = 0; i < base::size(kButtonTypes); ++i) {
NSButton* button = [NSWindow standardWindowButton:kButtonTypes[i]
forStyleMask:NSWindowStyleMaskTitled];
[button setTag:i];
int left_index = is_rtl_ ? base::size(kButtonTypes) - i - 1 : i;
[button setFrameOrigin:NSMakePoint(left_index * kButtonPadding, 0)];
[self addSubview:button];
}
NSView* last_button =
is_rtl_ ? [[self subviews] firstObject] : [[self subviews] lastObject];
[self setFrameSize:NSMakeSize(last_button.frame.origin.x +
last_button.frame.size.width,
last_button.frame.size.height)];
[self setNeedsDisplayForButtons];
return self;
}
- (void)setMargin:(const base::Optional<gfx::Point>&)margin {
margin_ = margin.value_or(gfx::Point(7, 3));
}
- (void)setShowOnHover:(BOOL)yes {
show_on_hover_ = yes;
[self setNeedsDisplayForButtons];
}
- (void)setNeedsDisplayForButtons {
for (NSView* subview in self.subviews) {
[subview setHidden:(show_on_hover_ && !mouse_inside_)];
[subview setNeedsDisplay:YES];
}
}
- (void)removeFromSuperview {
[super removeFromSuperview];
mouse_inside_ = NO;
}
- (void)viewDidMoveToWindow {
// Stay in upper left corner.
CGFloat y =
self.superview.frame.size.height - self.frame.size.height - margin_.y();
if (is_rtl_) {
CGFloat x =
self.superview.frame.size.width - self.frame.size.width - margin_.x();
[self setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
[self setFrameOrigin:NSMakePoint(x, y)];
} else {
[self setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin];
[self setFrameOrigin:NSMakePoint(margin_.x(), y)];
}
}
- (BOOL)_mouseInGroup:(NSButton*)button {
return mouse_inside_;
}
- (void)updateTrackingAreas {
[super updateTrackingAreas];
if (tracking_area_)
[self removeTrackingArea:tracking_area_.get()];
tracking_area_.reset([[NSTrackingArea alloc]
initWithRect:NSZeroRect
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways |
NSTrackingInVisibleRect
owner:self
userInfo:nil]);
[self addTrackingArea:tracking_area_.get()];
}
- (void)mouseEntered:(NSEvent*)event {
[super mouseEntered:event];
mouse_inside_ = YES;
[self setNeedsDisplayForButtons];
}
- (void)mouseExited:(NSEvent*)event {
[super mouseExited:event];
mouse_inside_ = NO;
[self setNeedsDisplayForButtons];
}
@end