From 377ae66d109d807b9158c0bd26c09489001e26ad Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 23 Jan 2020 10:13:08 -0800 Subject: [PATCH 1/2] feat: custom positioning for traffic light buttons (#21781) * feat: custom positioning for traffic light buttons * remove NSLog and unnecessary call-site in IsVisible() * no longer need to check if entering fullscreen * change API to take a point object Co-authored-by: tonyfwoo <55114329+tonyfwoo@users.noreply.github.com> --- docs/api/browser-window.md | 1 + shell/browser/native_window_mac.h | 7 +++ shell/browser/native_window_mac.mm | 48 +++++++++++++++++++ .../ui/cocoa/atom_ns_window_delegate.mm | 11 +++++ shell/common/options_switches.cc | 1 + shell/common/options_switches.h | 1 + 6 files changed, 69 insertions(+) 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..25ccf2bf8e11e 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,7 @@ 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_); + options.Get(options::kTrafficLightPosition, &traffic_light_position_); bool minimizable = true; options.Get(options::kMinimizable, &minimizable); @@ -511,6 +513,45 @@ 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]; + 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 +671,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 +996,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[]; From 172f7b1eebb21c39188cef4e38c0927bd44f65fb Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Thu, 30 Jan 2020 15:49:05 -0800 Subject: [PATCH 2/2] chore: add safety checks to RepositionTrafficLights --- shell/browser/native_window_mac.mm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 25ccf2bf8e11e..9d7bbe4492db3 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -338,7 +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_); - options.Get(options::kTrafficLightPosition, &traffic_light_position_); + 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); @@ -523,6 +527,12 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { 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