diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index ef6b64d023a3f..b0cb3f04c940f 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -229,6 +229,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. 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. + * `trafficLightPosition` [Point](structures/point.md) (optional) - Set a custom position for the traffic light buttons. Can only be used with `titleBarStyle` set to `hidden` * `fullscreenWindowTitle` Boolean (optional) - Shows the title in the title bar in full screen mode on macOS for all `titleBarStyle` options. Default is `false`. diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index c570eb212ab0d..4d29e5de01c6e 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -149,6 +149,10 @@ class NativeWindowMac : public NativeWindow { void SetCollectionBehavior(bool on, NSUInteger flag); void SetWindowLevel(int level); + // Custom traffic light positioning + void RepositionTrafficLights(); + void SetExitingFullScreen(bool flag); + enum class TitleBarStyle { NORMAL, HIDDEN, @@ -162,6 +166,7 @@ class NativeWindowMac : public NativeWindow { 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_; } protected: // views::WidgetDelegate: @@ -198,6 +203,8 @@ class NativeWindowMac : public NativeWindow { bool zoom_to_page_width_ = false; bool fullscreen_window_title_ = false; bool resizable_ = true; + bool exiting_fullscreen_ = false; + gfx::Point traffic_light_position_; NSInteger attention_request_id_ = 0; // identifier from requestUserAttention diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 2ee380cf90f7a..9d7bbe4492db3 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -31,6 +31,7 @@ #include "shell/browser/ui/inspectable_web_contents_view.h" #include "shell/browser/window_list.h" #include "shell/common/deprecate_util.h" +#include "shell/common/gin_converters/gfx_converter.h" #include "shell/common/options_switches.h" #include "skia/ext/skia_utils_mac.h" #include "third_party/webrtc/modules/desktop_capture/mac/window_list_utils.h" @@ -337,6 +338,11 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { options.Get(options::kZoomToPageWidth, &zoom_to_page_width_); options.Get(options::kFullscreenWindowTitle, &fullscreen_window_title_); options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_); + v8::Local traffic_light_options; + if (options.Get(options::kTrafficLightPosition, &traffic_light_options)) { + gin::ConvertFromV8(options.isolate(), traffic_light_options, + &traffic_light_position_); + } bool minimizable = true; options.Get(options::kMinimizable, &minimizable); @@ -511,6 +517,51 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { [NSEvent removeMonitor:wheel_event_monitor_]; } +void NativeWindowMac::RepositionTrafficLights() { + if (!traffic_light_position_.x() && !traffic_light_position_.y()) { + 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; + titleBarRect.size.height = titleBarFrameHeight; + titleBarRect.origin.y = window.frame.size.height - titleBarFrameHeight; + [titleBarContainerView setFrame:titleBarRect]; + + 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]; + rect.origin.x = traffic_light_position_.x() + (i * space_between); + rect.origin.y = (titleBarFrameHeight - rect.size.height) / 2; + [view setFrameOrigin:rect.origin]; + } +} + void NativeWindowMac::SetContentView(views::View* view) { views::View* root_view = GetContentsView(); if (content_view()) @@ -630,6 +681,10 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { return [window_ isVisible] && !occluded && !IsMinimized(); } +void NativeWindowMac::SetExitingFullScreen(bool flag) { + exiting_fullscreen_ = flag; +} + bool NativeWindowMac::IsEnabled() { return [window_ attachedSheet] == nil; } @@ -951,6 +1006,9 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { void NativeWindowMac::SetTitle(const std::string& title) { [window_ setTitle:base::SysUTF8ToNSString(title)]; + if (title_bar_style_ == TitleBarStyle::HIDDEN) { + RepositionTrafficLights(); + } } std::string NativeWindowMac::GetTitle() { diff --git a/shell/browser/ui/cocoa/atom_ns_window_delegate.mm b/shell/browser/ui/cocoa/atom_ns_window_delegate.mm index 927524e98121c..f715f9506855d 100644 --- a/shell/browser/ui/cocoa/atom_ns_window_delegate.mm +++ b/shell/browser/ui/cocoa/atom_ns_window_delegate.mm @@ -136,6 +136,9 @@ - (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize { - (void)windowDidResize:(NSNotification*)notification { [super windowDidResize:notification]; shell_->NotifyWindowResize(); + if (shell_->title_bar_style() == TitleBarStyle::HIDDEN) { + shell_->RepositionTrafficLights(); + } } - (void)windowWillMove:(NSNotification*)notification { @@ -249,11 +252,19 @@ - (void)windowWillExitFullScreen:(NSNotification*)notification { shell_->SetStyleMask(false, NSWindowStyleMaskFullSizeContentView); [window setTitlebarAppearsTransparent:YES]; } + shell_->SetExitingFullScreen(true); + if (shell_->title_bar_style() == TitleBarStyle::HIDDEN) { + shell_->RepositionTrafficLights(); + } } - (void)windowDidExitFullScreen:(NSNotification*)notification { shell_->SetResizable(is_resizable_); shell_->NotifyWindowLeaveFullScreen(); + shell_->SetExitingFullScreen(false); + if (shell_->title_bar_style() == TitleBarStyle::HIDDEN) { + shell_->RepositionTrafficLights(); + } } - (void)windowWillClose:(NSNotification*)notification { diff --git a/shell/common/options_switches.cc b/shell/common/options_switches.cc index a0b0de56e529a..3b09a5d2769b3 100644 --- a/shell/common/options_switches.cc +++ b/shell/common/options_switches.cc @@ -28,6 +28,7 @@ const char kMaximizable[] = "maximizable"; const char kFullScreenable[] = "fullscreenable"; const char kClosable[] = "closable"; const char kFullscreen[] = "fullscreen"; +const char kTrafficLightPosition[] = "trafficLightPosition"; // Whether the window should show in taskbar. const char kSkipTaskbar[] = "skipTaskbar"; diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index b678f03445049..d33a548ba887b 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -54,6 +54,7 @@ extern const char kOpacity[]; extern const char kFocusable[]; extern const char kWebPreferences[]; extern const char kVibrancyType[]; +extern const char kTrafficLightPosition[]; // WebPreferences. extern const char kZoomFactor[];