fix: move window buttons in-place on macOS (#30322)
This commit is contained in:
parent
1c29734c91
commit
97929eab5f
9 changed files with 334 additions and 278 deletions
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,25 +1495,26 @@ void NativeWindowMac::SetVibrancy(const std::string& type) {
|
|||
|
||||
void NativeWindowMac::SetWindowButtonVisibility(bool visible) {
|
||||
window_button_visibility_ = visible;
|
||||
InternalSetWindowButtonVisibility(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;
|
||||
return ![window_ standardWindowButton:NSWindowZoomButton].hidden ||
|
||||
![window_ standardWindowButton:NSWindowMiniaturizeButton].hidden ||
|
||||
![window_ standardWindowButton:NSWindowCloseButton].hidden;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -168,6 +168,7 @@ using FullScreenTransitionState =
|
|||
- (void)windowDidResize:(NSNotification*)notification {
|
||||
[super windowDidResize:notification];
|
||||
shell_->NotifyWindowResize();
|
||||
shell_->RedrawTrafficLights();
|
||||
}
|
||||
|
||||
- (void)windowWillMove:(NSNotification*)notification {
|
||||
|
|
66
shell/browser/ui/cocoa/window_buttons_proxy.h
Normal file
66
shell/browser/ui/cocoa/window_buttons_proxy.h
Normal 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_
|
200
shell/browser/ui/cocoa/window_buttons_proxy.mm
Normal file
200
shell/browser/ui/cocoa/window_buttons_proxy.mm
Normal 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
|
|
@ -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_
|
|
@ -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
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue