diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 9d34c6c31..d5d865b91 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 c4c4f09b5..0a7caa13e 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 3ba2ea8ab..377152c5c 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -28,6 +28,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/gin_helper/dictionary.h" #include "shell/common/options_switches.h" #include "skia/ext/skia_utils_mac.h" @@ -335,6 +336,7 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options, options.Get(options::kZoomToPageWidth, &zoom_to_page_width_); options.Get(options::kFullscreenWindowTitle, &fullscreen_window_title_); options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_); + options.Get(options::kTrafficLightPosition, &traffic_light_position_); bool minimizable = true; options.Get(options::kMinimizable, &minimizable); @@ -509,6 +511,45 @@ NativeWindowMac::~NativeWindowMac() { [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]; + 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()) @@ -628,6 +669,10 @@ bool NativeWindowMac::IsVisible() { return [window_ isVisible] && !occluded && !IsMinimized(); } +void NativeWindowMac::SetExitingFullScreen(bool flag) { + exiting_fullscreen_ = flag; +} + bool NativeWindowMac::IsEnabled() { return [window_ attachedSheet] == nil; } @@ -949,6 +994,9 @@ void NativeWindowMac::Invalidate() { 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 927524e98..f715f9506 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 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle; - (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 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle; 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 a0b0de56e..3b09a5d27 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 b678f0344..d33a548ba 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[];