From 8bf66f8974c6dc0aecdc22e4ddce2881e1836840 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 31 Jan 2021 08:15:10 +0900 Subject: [PATCH] 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 --- docs/api/browser-window.md | 23 +- filenames.gni | 2 + shell/browser/native_window.h | 4 +- shell/browser/native_window_mac.h | 16 +- shell/browser/native_window_mac.mm | 306 +++++------------- .../ui/cocoa/electron_ns_window_delegate.mm | 65 +--- shell/browser/ui/cocoa/window_buttons_view.h | 31 ++ shell/browser/ui/cocoa/window_buttons_view.mm | 119 +++++++ 8 files changed, 262 insertions(+), 304 deletions(-) create mode 100644 shell/browser/ui/cocoa/window_buttons_view.h create mode 100644 shell/browser/ui/cocoa/window_buttons_view.mm diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 08adb6ad84ca..8b22d4d2c57e 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -222,16 +222,14 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. the top left. * `hiddenInset` - Results in a hidden title bar with an alternative look where the traffic light buttons are slightly more inset from the window edge. - * `customButtonsOnHover` Boolean (optional) - Draw custom close, - and minimize buttons on macOS frameless windows. These buttons will not display - unless hovered over in the top left of the window. These custom buttons prevent - issues with mouse events that occur with the standard window toolbar buttons. - **Note:** This option is currently experimental. + * `customButtonsOnHover` - Results in a hidden title bar and a full size + content window, the traffic light buttons will display when being hovered + over in the top left of the window. **Note:** This option is currently + experimental. * `trafficLightPosition` [Point](structures/point.md) (optional) - Set a - custom position for the traffic light buttons. Can only be used with - `titleBarStyle` set to `hidden` or `customButtonsOnHover`. - * `fullscreenWindowTitle` Boolean (optional) - Shows the title in the - title bar in full screen mode on macOS for all `titleBarStyle` options. + custom position for the traffic light buttons in frameless windows. + * `fullscreenWindowTitle` Boolean (optional) _Deprecated_ - Shows the title in + the title bar in full screen mode on macOS for `hiddenInset` titleBarStyle. Default is `false`. * `thickFrame` Boolean (optional) - Use `WS_THICKFRAME` style for frameless windows on 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) -Set a custom position for the traffic light buttons. Can only be used with -`titleBarStyle` set to `hidden` or `customButtonsOnHover`. +Set a custom position for the traffic light buttons in frameless window. #### `win.getTrafficLightPosition()` _macOS_ -Returns `Point` - The current position for the traffic light buttons. Can only -be used with `titleBarStyle` set to `hidden` or `customButtonsOnHover`. +Returns `Point` - The custom position for the traffic light buttons in +frameless window. #### `win.setTouchBar(touchBar)` _macOS_ diff --git a/filenames.gni b/filenames.gni index bf09fbe614e5..a70b1137e0e4 100644 --- a/filenames.gni +++ b/filenames.gni @@ -180,6 +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/drag_util_mac.mm", "shell/browser/ui/file_dialog_mac.mm", "shell/browser/ui/inspectable_web_contents_view_mac.h", diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index b9ab07472bd9..6944f397e7d9 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -280,8 +280,8 @@ class NativeWindow : public base::SupportsUserData, void NotifyWindowRotateGesture(float rotation); void NotifyWindowSheetBegin(); void NotifyWindowSheetEnd(); - void NotifyWindowEnterFullScreen(); - void NotifyWindowLeaveFullScreen(); + virtual void NotifyWindowEnterFullScreen(); + virtual void NotifyWindowLeaveFullScreen(); void NotifyWindowEnterHtmlFullScreen(); void NotifyWindowLeaveHtmlFullScreen(); void NotifyWindowAlwaysOnTopChanged(); diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index 37cd3e62c1a8..92dee07ab30c 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -21,7 +21,7 @@ @class ElectronNSWindowDelegate; @class ElectronPreviewItem; @class ElectronTouchBar; -@class CustomWindowButtonView; +@class WindowButtonsView; namespace electron { @@ -139,6 +139,11 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { void CloseFilePreview() override; gfx::Rect ContentBoundsToWindowBounds(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 // 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 SetWindowLevel(int level); - void SetExitingFullScreen(bool flag); - enum class VisualEffectState { kFollowWindow, kActive, @@ -172,7 +175,6 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { ElectronPreviewItem* preview_item() const { return preview_item_.get(); } ElectronTouchBar* touch_bar() const { return touch_bar_.get(); } 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 exiting_fullscreen() const { return exiting_fullscreen_; } @@ -186,9 +188,10 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { private: // Add custom layers to the content view. - void AddContentViewLayers(bool minimizable, bool closable); + void AddContentViewLayers(); void InternalSetWindowButtonVisibility(bool visible); + void InternalSetStandardButtonsVisibility(bool visible); void InternalSetParentWindow(NativeWindow* parent, bool attach); void SetForwardMouseMessages(bool forward); @@ -197,7 +200,7 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { base::scoped_nsobject window_delegate_; base::scoped_nsobject preview_item_; base::scoped_nsobject touch_bar_; - base::scoped_nsobject buttons_view_; + base::scoped_nsobject buttons_view_; // Event monitor for scroll wheel event. id wheel_event_monitor_; @@ -213,7 +216,6 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { bool is_kiosk_ = false; bool was_fullscreen_ = false; bool zoom_to_page_width_ = false; - bool fullscreen_window_title_ = false; bool resizable_ = true; bool exiting_fullscreen_ = false; base::Optional traffic_light_position_; diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 3b50a6c64b9c..c2b423f5af47 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -29,6 +29,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/inspectable_web_contents.h" #include "shell/browser/ui/inspectable_web_contents_view.h" #include "shell/browser/window_list.h" @@ -119,106 +120,6 @@ @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&)margin; -- (void)setMargin:(const base::Optional&)margin; -@end - -@implementation CustomWindowButtonView - -- (id)initWithMargin:(const base::Optional&)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&)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 @end @@ -370,11 +271,17 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options, options.Get(options::kResizable, &resizable_); options.Get(options::kTitleBarStyle, &title_bar_style_); options.Get(options::kZoomToPageWidth, &zoom_to_page_width_); - options.Get(options::kFullscreenWindowTitle, &fullscreen_window_title_); options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_); options.GetOptional(options::kTrafficLightPosition, &traffic_light_position_); 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; options.Get(options::kMinimizable, &minimizable); @@ -457,6 +364,8 @@ 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); } // 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 toolbar( - [[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]); - [toolbar setShowsBaselineSeparator:NO]; - [window_ setToolbar:toolbar]; - } - // Resize to content bounds. bool use_content_size = false; options.Get(options::kUseContentSize, &use_content_size); @@ -527,7 +428,7 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options, // Default content view. SetContentView(new views::View()); - AddContentViewLayers(minimizable, closable); + AddContentViewLayers(); original_frame_ = [window_ frame]; original_level_ = [window_ level]; @@ -830,6 +731,8 @@ bool NativeWindowMac::IsMovable() { void NativeWindowMac::SetMinimizable(bool minimizable) { SetStyleMask(minimizable, NSMiniaturizableWindowMask); + if (buttons_view_) + [[buttons_view_ viewWithTag:1] setEnabled:minimizable]; } bool NativeWindowMac::IsMinimizable() { @@ -851,6 +754,8 @@ 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() { @@ -860,6 +765,8 @@ bool NativeWindowMac::IsFullScreenable() { void NativeWindowMac::SetClosable(bool closable) { SetStyleMask(closable, NSWindowStyleMaskClosable); + if (buttons_view_) + [[buttons_view_ viewWithTag:0] setEnabled:closable]; } bool NativeWindowMac::IsClosable() { @@ -940,9 +847,6 @@ void NativeWindowMac::Invalidate() { void NativeWindowMac::SetTitle(const std::string& title) { [window_ setTitle:base::SysUTF8ToNSString(title)]; - if (title_bar_style_ == TitleBarStyle::kHidden) { - RedrawTrafficLights(); - } } std::string NativeWindowMac::GetTitle() { @@ -1004,14 +908,7 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) { window.level = NSPopUpMenuWindowLevel; } - if (!fullscreen_window_title()) { - // 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()) { + if (!window_button_visibility_.has_value()) { // Lets keep previous behaviour - hide window controls in titled // fullscreen mode when not specified otherwise. InternalSetWindowButtonVisibility(false); @@ -1027,11 +924,6 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) { } else if (!simple_fullscreen && is_simple_fullscreen_) { is_simple_fullscreen_ = false; - if (!fullscreen_window_title()) { - // Restore the titlebar - SetStyleMask(true, NSWindowStyleMaskTitled); - } - // Restore default window controls visibility state. if (!window_button_visibility_.has_value()) { bool visibility; @@ -1450,9 +1342,7 @@ bool NativeWindowMac::GetWindowButtonVisibility() const { void NativeWindowMac::SetTrafficLightPosition( base::Optional position) { traffic_light_position_ = std::move(position); - if (title_bar_style_ == TitleBarStyle::kHidden) { - RedrawTrafficLights(); - } else if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover) { + if (buttons_view_) { [buttons_view_ setMargin:position]; [buttons_view_ viewDidMoveToWindow]; } @@ -1463,64 +1353,8 @@ base::Optional NativeWindowMac::GetTrafficLightPosition() const { } void NativeWindowMac::RedrawTrafficLights() { - // Ensure maximizable options retain pre-existing state. - SetMaximizable(maximizable_); - - // 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]; - } + if (buttons_view_) + [buttons_view_ setNeedsDisplayForButtons]; } 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() { DCHECK(!IsClosed()); ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this); @@ -1663,7 +1532,7 @@ void NativeWindowMac::OverrideNSWindowContentView() { [container_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; [window_ setContentView:container_view_]; - AddContentViewLayers(IsMinimizable(), IsClosable()); + AddContentViewLayers(); } void NativeWindowMac::SetStyleMask(bool on, NSUInteger flag) { @@ -1692,10 +1561,6 @@ void NativeWindowMac::SetCollectionBehavior(bool on, NSUInteger flag) { SetMaximizable(was_maximizable); } -void NativeWindowMac::SetExitingFullScreen(bool flag) { - exiting_fullscreen_ = flag; -} - bool NativeWindowMac::CanResize() const { return resizable_; } @@ -1710,7 +1575,7 @@ void NativeWindowMac::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) { 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: // http://crbug.com/396264. if (!is_modal()) { @@ -1742,46 +1607,39 @@ void NativeWindowMac::AddContentViewLayers(bool minimizable, bool closable) { [[window_ contentView] viewDidMoveToWindow]; } - // The fullscreen button should always be hidden for frameless window. - [[window_ standardWindowButton:NSWindowFullScreenButton] setHidden:YES]; + // 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:gfx::Point(12, 11)]; - // Create a custom window buttons view for kCustomButtonsOnHover. - if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover) { - buttons_view_.reset([[CustomWindowButtonView alloc] - initWithMargin:traffic_light_position_]); - - if (!minimizable) - [[buttons_view_ viewWithTag:2] removeFromSuperview]; - if (!closable) - [[buttons_view_ viewWithTag:1] removeFromSuperview]; + 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_]; } - - // 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) { - if (buttons_view_) { + if (buttons_view_) [buttons_view_ setHidden:!visible]; - } else { - [[window_ standardWindowButton:NSWindowCloseButton] setHidden:!visible]; - [[window_ standardWindowButton:NSWindowMiniaturizeButton] - setHidden:!visible]; - [[window_ standardWindowButton:NSWindowZoomButton] 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]; } void NativeWindowMac::InternalSetParentWindow(NativeWindow* parent, diff --git a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm index 899a25695f1f..5b8f6cb59cfa 100644 --- a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm +++ b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm @@ -91,14 +91,17 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle; - (void)windowDidBecomeMain:(NSNotification*)notification { shell_->NotifyWindowFocus(); + shell_->RedrawTrafficLights(); } - (void)windowDidResignMain:(NSNotification*)notification { shell_->NotifyWindowBlur(); + shell_->RedrawTrafficLights(); } - (void)windowDidBecomeKey:(NSNotification*)notification { shell_->NotifyWindowIsKeyChanged(true); + shell_->RedrawTrafficLights(); } - (void)windowDidResignKey:(NSNotification*)notification { @@ -110,6 +113,7 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle; return; shell_->NotifyWindowIsKeyChanged(false); + shell_->RedrawTrafficLights(); } - (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize { @@ -151,9 +155,6 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle; - (void)windowDidResize:(NSNotification*)notification { [super windowDidResize:notification]; shell_->NotifyWindowResize(); - if (shell_->title_bar_style() == TitleBarStyle::kHidden) { - shell_->RedrawTrafficLights(); - } } - (void)windowWillMove:(NSNotification*)notification { @@ -212,75 +213,23 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle; } - (void)windowWillEnterFullScreen:(NSNotification*)notification { - // Setting resizable to true before entering fullscreen + shell_->NotifyWindowWillEnterFullScreen(); + // 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. - if (shell_->title_bar_style() == TitleBarStyle::kHiddenInset) { - NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow(); - [window setToolbar:nil]; - } } - (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().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 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 { - // Restore the titlebar visibility. - 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(); - } + shell_->NotifyWindowWillLeaveFullScreen(); } - (void)windowDidExitFullScreen:(NSNotification*)notification { shell_->SetResizable(is_resizable_); shell_->NotifyWindowLeaveFullScreen(); - shell_->SetExitingFullScreen(false); - if (shell_->title_bar_style() == TitleBarStyle::kHidden) { - shell_->RedrawTrafficLights(); - } } - (void)windowWillClose:(NSNotification*)notification { diff --git a/shell/browser/ui/cocoa/window_buttons_view.h b/shell/browser/ui/cocoa/window_buttons_view.h new file mode 100644 index 000000000000..cc662bfc3450 --- /dev/null +++ b/shell/browser/ui/cocoa/window_buttons_view.h @@ -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 + +#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 tracking_area_; +} + +- (id)initWithMargin:(const base::Optional&)margin; +- (void)setMargin:(const base::Optional&)margin; +- (void)setShowOnHover:(BOOL)yes; +- (void)setNeedsDisplayForButtons; +@end + +#endif // SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_VIEW_H_ diff --git a/shell/browser/ui/cocoa/window_buttons_view.mm b/shell/browser/ui/cocoa/window_buttons_view.mm new file mode 100644 index 000000000000..f09054d615f0 --- /dev/null +++ b/shell/browser/ui/cocoa/window_buttons_view.mm @@ -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&)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&)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