Browse Source

feat: custom positioning for traffic light buttons (#21990)

* 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 <[email protected]>

* chore: add safety checks to RepositionTrafficLights

Co-authored-by: Tony <[email protected]>
Co-authored-by: tonyfwoo <[email protected]>
Samuel Attard 5 years ago
parent
commit
c5574c8667

+ 1 - 0
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`.

+ 7 - 0
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
 

+ 58 - 0
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 @@ NativeWindowMac::NativeWindowMac(const mate::Dictionary& options,
   options.Get(options::kZoomToPageWidth, &zoom_to_page_width_);
   options.Get(options::kFullscreenWindowTitle, &fullscreen_window_title_);
   options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_);
+  v8::Local<v8::Value> 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 @@ 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];
+  // 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 @@ bool NativeWindowMac::IsVisible() {
   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 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() {

+ 11 - 0
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 {

+ 1 - 0
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";

+ 1 - 0
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[];