fix: move window buttons in-place on macOS (#30322)

This commit is contained in:
Cheng Zhao 2021-08-04 09:31:12 +09:00 committed by GitHub
parent 1c29734c91
commit 97929eab5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 334 additions and 278 deletions

View file

@ -180,8 +180,8 @@ filenames = {
"shell/browser/ui/cocoa/root_view_mac.mm",
"shell/browser/ui/cocoa/views_delegate_mac.h",
"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/cocoa/window_buttons_proxy.h",
"shell/browser/ui/cocoa/window_buttons_proxy.mm",
"shell/browser/ui/drag_util_mac.mm",
"shell/browser/ui/file_dialog_mac.mm",
"shell/browser/ui/inspectable_web_contents_view_mac.h",

View file

@ -22,7 +22,7 @@
@class ElectronNSWindowDelegate;
@class ElectronPreviewItem;
@class ElectronTouchBar;
@class WindowButtonsView;
@class WindowButtonsProxy;
namespace electron {
@ -156,9 +156,6 @@ class NativeWindowMac : public NativeWindow,
void NotifyWindowWillEnterFullScreen();
void NotifyWindowWillLeaveFullScreen();
// Ensure the buttons view are always floated on the top.
void ReorderButtonsView();
// 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
// cleanup in destructor.
@ -215,7 +212,6 @@ class NativeWindowMac : public NativeWindow,
void AddContentViewLayers();
void InternalSetWindowButtonVisibility(bool visible);
void InternalSetStandardButtonsVisibility(bool visible);
void InternalSetParentWindow(NativeWindow* parent, bool attach);
void SetForwardMouseMessages(bool forward);
@ -224,7 +220,6 @@ class NativeWindowMac : public NativeWindow,
base::scoped_nsobject<ElectronNSWindowDelegate> window_delegate_;
base::scoped_nsobject<ElectronPreviewItem> preview_item_;
base::scoped_nsobject<ElectronTouchBar> touch_bar_;
base::scoped_nsobject<WindowButtonsView> buttons_view_;
// Event monitor for scroll wheel event.
id wheel_event_monitor_;
@ -263,6 +258,9 @@ class NativeWindowMac : public NativeWindow,
// setWindowButtonVisibility().
absl::optional<bool> window_button_visibility_;
// Controls the position and visibility of window buttons.
base::scoped_nsobject<WindowButtonsProxy> buttons_proxy_;
// Maximizable window state; necessary for persistence through redraws.
bool maximizable_ = true;

View file

@ -32,7 +32,7 @@
#include "shell/browser/ui/cocoa/electron_preview_item.h"
#include "shell/browser/ui/cocoa/electron_touch_bar.h"
#include "shell/browser/ui/cocoa/root_view_mac.h"
#include "shell/browser/ui/cocoa/window_buttons_view.h"
#include "shell/browser/ui/cocoa/window_buttons_proxy.h"
#include "shell/browser/ui/inspectable_web_contents.h"
#include "shell/browser/ui/inspectable_web_contents_view.h"
#include "shell/browser/window_list.h"
@ -381,8 +381,26 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
[window_ setTitleVisibility:NSWindowTitleHidden];
// Remove non-transparent corners, see http://git.io/vfonD.
[window_ setOpaque:NO];
// Hide the window buttons.
InternalSetStandardButtonsVisibility(false);
// Show window buttons if titleBarStyle is not "normal".
if (title_bar_style_ == TitleBarStyle::kNormal) {
InternalSetWindowButtonVisibility(false);
} else {
buttons_proxy_.reset([[WindowButtonsProxy alloc] initWithWindow:window_]);
if (traffic_light_position_) {
[buttons_proxy_ setMargin:*traffic_light_position_];
} else if (title_bar_style_ == TitleBarStyle::kHiddenInset) {
// For macOS >= 11, while this value does not match offical macOS apps
// like Safari or Notes, it matches titleBarStyle's old implementation
// before Electron <= 12.
[buttons_proxy_ setMargin:gfx::Point(12, 11)];
}
if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover) {
[buttons_proxy_ setShowOnHover:YES];
} else {
// customButtonsOnHover does not show buttons initialiy.
InternalSetWindowButtonVisibility(true);
}
}
}
// Create a tab only if tabbing identifier is specified and window has
@ -461,9 +479,6 @@ void NativeWindowMac::SetContentView(views::View* view) {
set_content_view(view);
root_view->AddChildView(content_view());
if (buttons_view_)
ReorderButtonsView();
root_view->Layout();
}
@ -806,8 +821,6 @@ bool NativeWindowMac::IsMovable() {
void NativeWindowMac::SetMinimizable(bool minimizable) {
SetStyleMask(minimizable, NSMiniaturizableWindowMask);
if (buttons_view_)
[[buttons_view_ viewWithTag:1] setEnabled:minimizable];
}
bool NativeWindowMac::IsMinimizable() {
@ -829,8 +842,6 @@ void NativeWindowMac::SetFullScreenable(bool fullscreenable) {
// On EL Capitan this flag is required to hide fullscreen button.
SetCollectionBehavior(!fullscreenable,
NSWindowCollectionBehaviorFullScreenAuxiliary);
if (buttons_view_)
[[buttons_view_ viewWithTag:2] setEnabled:fullscreenable];
}
bool NativeWindowMac::IsFullScreenable() {
@ -840,8 +851,6 @@ bool NativeWindowMac::IsFullScreenable() {
void NativeWindowMac::SetClosable(bool closable) {
SetStyleMask(closable, NSWindowStyleMaskClosable);
if (buttons_view_)
[[buttons_view_ viewWithTag:0] setEnabled:closable];
}
bool NativeWindowMac::IsClosable() {
@ -947,6 +956,8 @@ void NativeWindowMac::Invalidate() {
void NativeWindowMac::SetTitle(const std::string& title) {
[window_ setTitle:base::SysUTF8ToNSString(title)];
if (buttons_proxy_)
[buttons_proxy_ redraw];
}
std::string NativeWindowMac::GetTitle() {
@ -1182,8 +1193,6 @@ void NativeWindowMac::AddBrowserView(NativeBrowserView* view) {
}
[CATransaction commit];
ReorderButtonsView();
}
void NativeWindowMac::RemoveBrowserView(NativeBrowserView* view) {
@ -1225,8 +1234,6 @@ void NativeWindowMac::SetTopBrowserView(NativeBrowserView* view) {
}
[CATransaction commit];
ReorderButtonsView();
}
void NativeWindowMac::SetParentWindow(NativeWindow* parent) {
@ -1488,14 +1495,16 @@ void NativeWindowMac::SetVibrancy(const std::string& type) {
void NativeWindowMac::SetWindowButtonVisibility(bool visible) {
window_button_visibility_ = visible;
// The visibility of window buttons are managed by |buttons_proxy_| if the
// style is customButtonsOnHover.
if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover)
[buttons_proxy_ setVisible:visible];
else
InternalSetWindowButtonVisibility(visible);
NotifyLayoutWindowControlsOverlay();
}
bool NativeWindowMac::GetWindowButtonVisibility() const {
if (buttons_view_)
return ![buttons_view_ isHidden];
else
return ![window_ standardWindowButton:NSWindowZoomButton].hidden ||
![window_ standardWindowButton:NSWindowMiniaturizeButton].hidden ||
![window_ standardWindowButton:NSWindowCloseButton].hidden;
@ -1504,9 +1513,8 @@ bool NativeWindowMac::GetWindowButtonVisibility() const {
void NativeWindowMac::SetTrafficLightPosition(
absl::optional<gfx::Point> position) {
traffic_light_position_ = std::move(position);
if (buttons_view_) {
[buttons_view_ setMargin:traffic_light_position_];
[buttons_view_ viewDidMoveToWindow];
if (buttons_proxy_) {
[buttons_proxy_ setMargin:traffic_light_position_];
NotifyLayoutWindowControlsOverlay();
}
}
@ -1516,8 +1524,8 @@ absl::optional<gfx::Point> NativeWindowMac::GetTrafficLightPosition() const {
}
void NativeWindowMac::RedrawTrafficLights() {
if (buttons_view_)
[buttons_view_ setNeedsDisplayForButtons];
if (buttons_proxy_ && !IsFullscreen())
[buttons_proxy_ redraw];
}
// In simpleFullScreen mode, update the frame for new bounds.
@ -1649,35 +1657,30 @@ gfx::Rect NativeWindowMac::WindowBoundsToContentBounds(
void NativeWindowMac::NotifyWindowEnterFullScreen() {
NativeWindow::NotifyWindowEnterFullScreen();
// Restore the window title under fullscreen mode.
if (buttons_view_)
if (buttons_proxy_)
[window_ setTitleVisibility:NSWindowTitleVisible];
RedrawTrafficLights();
}
void NativeWindowMac::NotifyWindowLeaveFullScreen() {
NativeWindow::NotifyWindowLeaveFullScreen();
// Restore window buttons.
if (buttons_proxy_ && window_button_visibility_.value_or(true)) {
[buttons_proxy_ redraw];
[buttons_proxy_ setVisible:YES];
}
}
void NativeWindowMac::NotifyWindowWillEnterFullScreen() {
// Remove the buttonsView otherwise window buttons won't show under
// fullscreen mode.
if (buttons_view_) {
[buttons_view_ removeFromSuperview];
InternalSetStandardButtonsVisibility(true);
}
UpdateVibrancyRadii(true);
}
void NativeWindowMac::NotifyWindowWillLeaveFullScreen() {
// Hide window title and restore buttonsView when leaving fullscreen.
if (buttons_view_) {
if (buttons_proxy_) {
// Hide window title when leaving fullscreen.
[window_ setTitleVisibility:NSWindowTitleHidden];
InternalSetStandardButtonsVisibility(false);
[[window_ contentView] addSubview:buttons_view_];
// Hide the container otherwise traffic light buttons jump.
[buttons_proxy_ setVisible:NO];
}
RedrawTrafficLights();
UpdateVibrancyRadii(false);
}
@ -1689,13 +1692,6 @@ bool NativeWindowMac::IsActive() const {
return is_active_;
}
void NativeWindowMac::ReorderButtonsView() {
if (buttons_view_ && !IsFullscreen()) {
[buttons_view_ removeFromSuperview];
[[window_ contentView] addSubview:buttons_view_];
}
}
void NativeWindowMac::Cleanup() {
DCHECK(!IsClosed());
ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this);
@ -1791,37 +1787,10 @@ void NativeWindowMac::AddContentViewLayers() {
(IMP)ViewDidMoveToSuperview, "v@:");
[[window_ contentView] viewDidMoveToWindow];
}
// Create a custom window buttons view.
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:[WindowButtonsView hiddenInsetMargin]];
if (!IsClosable())
[[buttons_view_ viewWithTag:0] setEnabled:NO];
if (!IsMinimizable())
[[buttons_view_ viewWithTag:1] setEnabled:NO];
if (!IsFullScreenable())
[[buttons_view_ viewWithTag:2] setEnabled:NO];
[[window_ contentView] addSubview:buttons_view_];
}
}
}
void NativeWindowMac::InternalSetWindowButtonVisibility(bool visible) {
if (buttons_view_)
[buttons_view_ setHidden:!visible];
else
InternalSetStandardButtonsVisibility(visible);
}
void NativeWindowMac::InternalSetStandardButtonsVisibility(bool visible) {
[[window_ standardWindowButton:NSWindowCloseButton] setHidden:!visible];
[[window_ standardWindowButton:NSWindowMiniaturizeButton] setHidden:!visible];
[[window_ standardWindowButton:NSWindowZoomButton] setHidden:!visible];
@ -1861,24 +1830,17 @@ void NativeWindowMac::SetForwardMouseMessages(bool forward) {
}
gfx::Rect NativeWindowMac::GetWindowControlsOverlayRect() {
gfx::Rect bounding_rect;
if (titlebar_overlay_ && !has_frame() && buttons_view_ &&
![buttons_view_ isHidden]) {
NSRect button_frame = [buttons_view_ frame];
gfx::Point buttons_view_margin = [buttons_view_ getMargin];
const int overlay_width = GetContentSize().width() - NSWidth(button_frame) -
buttons_view_margin.x();
CGFloat overlay_height =
NSHeight(button_frame) + buttons_view_margin.y() * 2;
if (base::i18n::IsRTL()) {
bounding_rect = gfx::Rect(0, 0, overlay_width, overlay_height);
} else {
bounding_rect =
gfx::Rect(button_frame.size.width + buttons_view_margin.x(), 0,
overlay_width, overlay_height);
if (titlebar_overlay_ && buttons_proxy_ &&
window_button_visibility_.value_or(true)) {
NSRect buttons = [buttons_proxy_ getButtonsContainerBounds];
gfx::Rect overlay;
overlay.set_width(GetContentSize().width() - NSWidth(buttons));
overlay.set_height(NSHeight(buttons));
if (!base::i18n::IsRTL())
overlay.set_x(NSMaxX(buttons));
return overlay;
}
}
return bounding_rect;
return gfx::Rect();
}
// static

View file

@ -168,6 +168,7 @@ using FullScreenTransitionState =
- (void)windowDidResize:(NSNotification*)notification {
[super windowDidResize:notification];
shell_->NotifyWindowResize();
shell_->RedrawTrafficLights();
}
- (void)windowWillMove:(NSNotification*)notification {

View file

@ -0,0 +1,66 @@
// 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_PROXY_H_
#define SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_PROXY_H_
#import <Cocoa/Cocoa.h>
#include "base/mac/scoped_nsobject.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/point.h"
@class WindowButtonsProxy;
// A helper view that floats above the window buttons.
@interface ButtonsAreaHoverView : NSView {
@private
WindowButtonsProxy* proxy_;
}
- (id)initWithProxy:(WindowButtonsProxy*)proxy;
@end
// Manipulating the window buttons.
@interface WindowButtonsProxy : NSObject {
@private
NSWindow* window_;
// The view that contains the window buttons and title.
NSView* titlebar_container_;
// The window buttons.
NSButton* left_;
NSButton* right_;
NSButton* middle_;
// Current left-top margin of buttons.
gfx::Point margin_;
// The default left-top margin.
gfx::Point default_margin_;
// Track mouse moves above window buttons.
BOOL show_on_hover_;
BOOL mouse_inside_;
base::scoped_nsobject<NSTrackingArea> tracking_area_;
base::scoped_nsobject<ButtonsAreaHoverView> hover_view_;
}
- (id)initWithWindow:(NSWindow*)window;
- (void)setVisible:(BOOL)visible;
- (BOOL)isVisible;
// Only show window buttons when mouse moves above them.
- (void)setShowOnHover:(BOOL)yes;
// Set left-top margin of the window buttons..
- (void)setMargin:(const absl::optional<gfx::Point>&)margin;
// Return the bounds of all 3 buttons, with margin on all sides.
- (NSRect)getButtonsContainerBounds;
- (void)redraw;
- (void)updateTrackingAreas;
@end
#endif // SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_PROXY_H_

View file

@ -0,0 +1,200 @@
// 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_proxy.h"
#include "base/i18n/rtl.h"
#include "base/notreached.h"
@implementation ButtonsAreaHoverView : NSView
- (id)initWithProxy:(WindowButtonsProxy*)proxy {
if ((self = [super init])) {
proxy_ = proxy;
}
return self;
}
// Ignore all mouse events.
- (NSView*)hitTest:(NSPoint)aPoint {
return nil;
}
- (void)updateTrackingAreas {
[proxy_ updateTrackingAreas];
}
@end
@implementation WindowButtonsProxy
- (id)initWithWindow:(NSWindow*)window {
window_ = window;
show_on_hover_ = NO;
mouse_inside_ = NO;
// Save the sequence of the buttons for later computation.
if (base::i18n::IsRTL()) {
left_ = [window_ standardWindowButton:NSWindowZoomButton];
right_ = [window_ standardWindowButton:NSWindowCloseButton];
} else {
left_ = [window_ standardWindowButton:NSWindowCloseButton];
right_ = [window_ standardWindowButton:NSWindowZoomButton];
}
middle_ = [window_ standardWindowButton:NSWindowMiniaturizeButton];
// Safety check just in case Apple changes the view structure in a macOS
// upgrade.
if (!left_.superview || !left_.superview.superview) {
NOTREACHED() << "macOS has changed its window buttons view structure.";
titlebar_container_ = nullptr;
return self;
}
titlebar_container_ = left_.superview.superview;
// Remember the default margin.
margin_ = default_margin_ = [self getCurrentMargin];
return self;
}
- (void)dealloc {
if (hover_view_)
[hover_view_ removeFromSuperview];
[super dealloc];
}
- (void)setVisible:(BOOL)visible {
if (!titlebar_container_)
return;
[titlebar_container_ setHidden:!visible];
}
- (BOOL)isVisible {
if (!titlebar_container_)
return YES;
return ![titlebar_container_ isHidden];
}
- (void)setShowOnHover:(BOOL)yes {
if (!titlebar_container_)
return;
show_on_hover_ = yes;
// Put a transparent view above the window buttons so we can track mouse
// events when mouse enter/leave the window buttons.
if (show_on_hover_) {
hover_view_.reset([[ButtonsAreaHoverView alloc] initWithProxy:self]);
[hover_view_ setFrame:[self getButtonsBounds]];
[titlebar_container_ addSubview:hover_view_.get()];
} else {
[hover_view_ removeFromSuperview];
hover_view_.reset();
}
[self updateButtonsVisibility];
}
- (void)setMargin:(const absl::optional<gfx::Point>&)margin {
if (margin)
margin_ = *margin;
else
margin_ = default_margin_;
[self redraw];
}
- (NSRect)getButtonsContainerBounds {
return NSInsetRect([self getButtonsBounds], -margin_.x(), -margin_.y());
}
- (void)redraw {
if (!titlebar_container_)
return;
float button_width = NSWidth(left_.frame);
float button_height = NSHeight(left_.frame);
float padding = NSMinX(middle_.frame) - NSMaxX(left_.frame);
float start;
if (base::i18n::IsRTL())
start =
NSWidth(window_.frame) - 3 * button_width - 2 * padding - margin_.x();
else
start = margin_.x();
NSRect cbounds = titlebar_container_.frame;
cbounds.size.height = button_height + 2 * margin_.y();
cbounds.origin.y = NSHeight(window_.frame) - NSHeight(cbounds);
[titlebar_container_ setFrame:cbounds];
[left_ setFrameOrigin:NSMakePoint(start, margin_.y())];
start += button_width + padding;
[middle_ setFrameOrigin:NSMakePoint(start, margin_.y())];
start += button_width + padding;
[right_ setFrameOrigin:NSMakePoint(start, margin_.y())];
if (hover_view_)
[hover_view_ setFrame:[self getButtonsBounds]];
}
- (void)updateTrackingAreas {
if (tracking_area_)
[hover_view_ removeTrackingArea:tracking_area_.get()];
tracking_area_.reset([[NSTrackingArea alloc]
initWithRect:NSZeroRect
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways |
NSTrackingInVisibleRect
owner:self
userInfo:nil]);
[hover_view_ addTrackingArea:tracking_area_.get()];
}
- (void)mouseEntered:(NSEvent*)event {
mouse_inside_ = YES;
[self updateButtonsVisibility];
}
- (void)mouseExited:(NSEvent*)event {
mouse_inside_ = NO;
[self updateButtonsVisibility];
}
- (void)updateButtonsVisibility {
NSArray* buttons = @[
[window_ standardWindowButton:NSWindowCloseButton],
[window_ standardWindowButton:NSWindowMiniaturizeButton],
[window_ standardWindowButton:NSWindowZoomButton],
];
// Show buttons when mouse hovers above them.
BOOL hidden = show_on_hover_ && !mouse_inside_;
// Always show buttons under fullscreen.
if ([window_ styleMask] & NSWindowStyleMaskFullScreen)
hidden = NO;
for (NSView* button in buttons) {
[button setHidden:hidden];
[button setNeedsDisplay:YES];
}
}
// Return the bounds of all 3 buttons.
- (NSRect)getButtonsBounds {
return NSMakeRect(NSMinX(left_.frame), NSMinY(left_.frame),
NSMaxX(right_.frame) - NSMinX(left_.frame),
NSHeight(left_.frame));
}
// Compute margin from position of current buttons.
- (gfx::Point)getCurrentMargin {
gfx::Point result;
if (!titlebar_container_)
return result;
result.set_y((NSHeight(titlebar_container_.frame) - NSHeight(left_.frame)) /
2);
if (base::i18n::IsRTL())
result.set_x(NSWidth(window_.frame) - NSMaxX(right_.frame));
else
result.set_x(NSMinX(left_.frame));
return result;
}
@end

View file

@ -1,34 +0,0 @@
// 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 "third_party/abseil-cpp/absl/types/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_;
}
+ (gfx::Point)defaultMargin;
+ (gfx::Point)hiddenInsetMargin;
- (id)initWithMargin:(const absl::optional<gfx::Point>&)margin;
- (void)setMargin:(const absl::optional<gfx::Point>&)margin;
- (void)setShowOnHover:(BOOL)yes;
- (void)setNeedsDisplayForButtons;
- (gfx::Point)getMargin;
@end
#endif // SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_VIEW_H_

View file

@ -1,138 +0,0 @@
// 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/cxx17_backports.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "ui/gfx/mac/coordinate_conversion.h"
namespace {
const CGFloat kButtonPadding = 20.;
const NSWindowButton kButtonTypes[] = {
NSWindowCloseButton,
NSWindowMiniaturizeButton,
NSWindowZoomButton,
};
} // namespace
@implementation WindowButtonsView
+ (gfx::Point)defaultMargin {
if (@available(macOS 11.0, *)) {
return gfx::Point(7, 6);
} else {
return gfx::Point(7, 3);
}
}
+ (gfx::Point)hiddenInsetMargin {
// For macOS >= 11, while this value does not match offical macOS apps like
// Safari or Notes, it matches titleBarStyle's old implementation before
// Electron <= 12.
return gfx::Point(12, 11);
}
- (id)initWithMargin:(const absl::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 absl::optional<gfx::Point>&)margin {
margin_ = margin.value_or([WindowButtonsView defaultMargin]);
}
- (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];
}
- (gfx::Point)getMargin {
return margin_;
}
@end

View file

@ -1593,13 +1593,14 @@ describe('BrowserWindow module', () => {
expect(w._getWindowButtonVisibility()).to.equal(true);
});
it('changes window button visibility for customButtonsOnHover window', () => {
// Buttons of customButtonsOnHover are always hidden unless hovered.
it('does not change window button visibility for customButtonsOnHover window', () => {
const w = new BrowserWindow({ show: false, frame: false, titleBarStyle: 'customButtonsOnHover' });
expect(w._getWindowButtonVisibility()).to.equal(true);
w.setWindowButtonVisibility(false);
expect(w._getWindowButtonVisibility()).to.equal(false);
w.setWindowButtonVisibility(true);
expect(w._getWindowButtonVisibility()).to.equal(true);
expect(w._getWindowButtonVisibility()).to.equal(false);
w.setWindowButtonVisibility(false);
expect(w._getWindowButtonVisibility()).to.equal(false);
});
});