Browse Source

feat: add BrowserWindow.set/getWindowButtonPosition APIs (#37094)

Cheng Zhao 2 years ago
parent
commit
0de1012280

+ 23 - 3
docs/api/browser-window.md

@@ -1842,16 +1842,36 @@ will remove the vibrancy effect on the window.
 Note that `appearance-based`, `light`, `dark`, `medium-light`, and `ultra-dark` have been
 deprecated and will be removed in an upcoming version of macOS.
 
-#### `win.setTrafficLightPosition(position)` _macOS_
+#### `win.setWindowButtonPosition(position)` _macOS_
+
+* `position` [Point](structures/point.md) | null
+
+Set a custom position for the traffic light buttons in frameless window.
+Passing `null` will reset the position to default.
+
+#### `win.getWindowButtonPosition()` _macOS_
+
+Returns `Point | null` - The custom position for the traffic light buttons in
+frameless window, `null` will be returned when there is no custom position.
+
+#### `win.setTrafficLightPosition(position)` _macOS_ _Deprecated_
 
 * `position` [Point](structures/point.md)
 
 Set a custom position for the traffic light buttons in frameless window.
+Passing `{ x: 0, y: 0 }` will reset the position to default.
+
+> **Note**
+> This function is deprecated. Use [setWindowButtonPosition](#winsetwindowbuttonpositionposition-macos) instead.
 
-#### `win.getTrafficLightPosition()` _macOS_
+#### `win.getTrafficLightPosition()` _macOS_ _Deprecated_
 
 Returns `Point` - The custom position for the traffic light buttons in
-frameless window.
+frameless window, `{ x: 0, y: 0 }` will be returned when there is no custom
+position.
+
+> **Note**
+> This function is deprecated. Use [getWindowButtonPosition](#wingetwindowbuttonposition-macos) instead.
 
 #### `win.setTouchBar(touchBar)` _macOS_
 

+ 40 - 0
docs/breaking-changes.md

@@ -12,6 +12,46 @@ This document uses the following convention to categorize breaking changes:
 * **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
 * **Removed:** An API or feature was removed, and is no longer supported by Electron.
 
+## Planned Breaking API Changes (24.0)
+
+### Deprecated: `BrowserWindow.setTrafficLightPosition(position)`
+
+`BrowserWindow.setTrafficLightPosition(position)` has been deprecated, the
+`BrowserWindow.setWindowButtonPosition(position)` API should be used instead
+which accepts `null` instead of `{ x: 0, y: 0 }` to reset the position to
+system default.
+
+```js
+// Removed in Electron 24
+win.setTrafficLightPosition({ x: 10, y: 10 })
+win.setTrafficLightPosition({ x: 0, y: 0 })
+
+// Replace with
+win.setWindowButtonPosition({ x: 10, y: 10 })
+win.setWindowButtonPosition(null)
+```
+
+### Deprecated: `BrowserWindow.getTrafficLightPosition()`
+
+`BrowserWindow.getTrafficLightPosition()` has been deprecated, the
+`BrowserWindow.getWindowButtonPosition()` API should be used instead
+which returns `null` instead of `{ x: 0, y: 0 }` when there is no custom
+position.
+
+```js
+// Removed in Electron 24
+const pos = win.getTrafficLightPosition()
+if (pos.x === 0 && pos.y === 0) {
+  // No custom position.
+}
+
+// Replace with
+const ret = win.getWindowButtonPosition()
+if (ret === null) {
+  // No custom position.
+}
+```
+
 ## Planned Breaking API Changes (23.0)
 
 ### Removed: Windows 7 / 8 / 8.1 support

+ 1 - 0
filenames.gni

@@ -582,6 +582,7 @@ filenames = {
     "shell/common/gin_converters/native_window_converter.h",
     "shell/common/gin_converters/net_converter.cc",
     "shell/common/gin_converters/net_converter.h",
+    "shell/common/gin_converters/optional_converter.h",
     "shell/common/gin_converters/serial_port_info_converter.h",
     "shell/common/gin_converters/std_converter.h",
     "shell/common/gin_converters/time_converter.cc",

+ 20 - 0
lib/browser/api/base-window.ts

@@ -1,5 +1,6 @@
 import { EventEmitter } from 'events';
 import type { BaseWindow as TLWT } from 'electron/main';
+import * as deprecate from '@electron/internal/common/deprecate';
 const { BaseWindow } = process._linkedBinding('electron_browser_base_window') as { BaseWindow: typeof TLWT };
 
 Object.setPrototypeOf(BaseWindow.prototype, EventEmitter.prototype);
@@ -15,6 +16,25 @@ BaseWindow.prototype._init = function () {
   }
 };
 
+// Deprecation.
+const setTrafficLightPositionDeprecated = deprecate.warnOnce('setTrafficLightPosition', 'setWindowButtonPosition');
+// Converting to any as the methods are defined under BrowserWindow in our docs.
+(BaseWindow as any).prototype.setTrafficLightPosition = function (pos: Electron.Point) {
+  setTrafficLightPositionDeprecated();
+  if (typeof pos === 'object' && pos.x === 0 && pos.y === 0) {
+    this.setWindowButtonPosition(null);
+  } else {
+    this.setWindowButtonPosition(pos);
+  }
+};
+
+const getTrafficLightPositionDeprecated = deprecate.warnOnce('getTrafficLightPosition', 'getWindowButtonPosition');
+(BaseWindow as any).prototype.getTrafficLightPosition = function () {
+  getTrafficLightPositionDeprecated();
+  const pos = this.getWindowButtonPosition();
+  return pos === null ? { x: 0, y: 0 } : pos;
+};
+
 // Properties
 
 Object.defineProperty(BaseWindow.prototype, 'autoHideMenuBar', {

+ 9 - 15
shell/browser/api/electron_api_base_window.cc

@@ -22,6 +22,7 @@
 #include "shell/common/gin_converters/gfx_converter.h"
 #include "shell/common/gin_converters/image_converter.h"
 #include "shell/common/gin_converters/native_window_converter.h"
+#include "shell/common/gin_converters/optional_converter.h"
 #include "shell/common/gin_converters/value_converter.h"
 #include "shell/common/gin_helper/dictionary.h"
 #include "shell/common/gin_helper/object_template_builder.h"
@@ -876,17 +877,12 @@ bool BaseWindow::GetWindowButtonVisibility() const {
   return window_->GetWindowButtonVisibility();
 }
 
-void BaseWindow::SetTrafficLightPosition(const gfx::Point& position) {
-  // For backward compatibility we treat (0, 0) as resetting to default.
-  if (position.IsOrigin())
-    window_->SetTrafficLightPosition(absl::nullopt);
-  else
-    window_->SetTrafficLightPosition(position);
+void BaseWindow::SetWindowButtonPosition(absl::optional<gfx::Point> position) {
+  window_->SetWindowButtonPosition(std::move(position));
 }
 
-gfx::Point BaseWindow::GetTrafficLightPosition() const {
-  // For backward compatibility we treat default value as (0, 0).
-  return window_->GetTrafficLightPosition().value_or(gfx::Point());
+absl::optional<gfx::Point> BaseWindow::GetWindowButtonPosition() const {
+  return window_->GetWindowButtonPosition();
 }
 #endif
 
@@ -1271,12 +1267,6 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate,
       .SetMethod("setAutoHideCursor", &BaseWindow::SetAutoHideCursor)
 #endif
       .SetMethod("setVibrancy", &BaseWindow::SetVibrancy)
-#if BUILDFLAG(IS_MAC)
-      .SetMethod("setTrafficLightPosition",
-                 &BaseWindow::SetTrafficLightPosition)
-      .SetMethod("getTrafficLightPosition",
-                 &BaseWindow::GetTrafficLightPosition)
-#endif
 
 #if BUILDFLAG(IS_MAC)
       .SetMethod("isHiddenInMissionControl",
@@ -1299,6 +1289,10 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate,
                  &BaseWindow::SetWindowButtonVisibility)
       .SetMethod("_getWindowButtonVisibility",
                  &BaseWindow::GetWindowButtonVisibility)
+      .SetMethod("setWindowButtonPosition",
+                 &BaseWindow::SetWindowButtonPosition)
+      .SetMethod("getWindowButtonPosition",
+                 &BaseWindow::GetWindowButtonPosition)
       .SetProperty("excludedFromShownWindowsMenu",
                    &BaseWindow::IsExcludedFromShownWindowsMenu,
                    &BaseWindow::SetExcludedFromShownWindowsMenu)

+ 2 - 2
shell/browser/api/electron_api_base_window.h

@@ -195,8 +195,8 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
   std::string GetAlwaysOnTopLevel();
   void SetWindowButtonVisibility(bool visible);
   bool GetWindowButtonVisibility() const;
-  void SetTrafficLightPosition(const gfx::Point& position);
-  gfx::Point GetTrafficLightPosition() const;
+  void SetWindowButtonPosition(absl::optional<gfx::Point> position);
+  absl::optional<gfx::Point> GetWindowButtonPosition() const;
 #endif
 
 #if BUILDFLAG(IS_MAC)

+ 2 - 2
shell/browser/native_window.h

@@ -219,8 +219,8 @@ class NativeWindow : public base::SupportsUserData,
 #if BUILDFLAG(IS_MAC)
   virtual void SetWindowButtonVisibility(bool visible) = 0;
   virtual bool GetWindowButtonVisibility() const = 0;
-  virtual void SetTrafficLightPosition(absl::optional<gfx::Point> position) = 0;
-  virtual absl::optional<gfx::Point> GetTrafficLightPosition() const = 0;
+  virtual void SetWindowButtonPosition(absl::optional<gfx::Point> position) = 0;
+  virtual absl::optional<gfx::Point> GetWindowButtonPosition() const = 0;
   virtual void RedrawTrafficLights() = 0;
   virtual void UpdateFrame() = 0;
 #endif

+ 2 - 2
shell/browser/native_window_mac.h

@@ -129,8 +129,8 @@ class NativeWindowMac : public NativeWindow,
   void SetVibrancy(const std::string& type) override;
   void SetWindowButtonVisibility(bool visible) override;
   bool GetWindowButtonVisibility() const override;
-  void SetTrafficLightPosition(absl::optional<gfx::Point> position) override;
-  absl::optional<gfx::Point> GetTrafficLightPosition() const override;
+  void SetWindowButtonPosition(absl::optional<gfx::Point> position) override;
+  absl::optional<gfx::Point> GetWindowButtonPosition() const override;
   void RedrawTrafficLights() override;
   void UpdateFrame() override;
   void SetTouchBar(

+ 2 - 2
shell/browser/native_window_mac.mm

@@ -1474,7 +1474,7 @@ bool NativeWindowMac::GetWindowButtonVisibility() const {
          ![window_ standardWindowButton:NSWindowCloseButton].hidden;
 }
 
-void NativeWindowMac::SetTrafficLightPosition(
+void NativeWindowMac::SetWindowButtonPosition(
     absl::optional<gfx::Point> position) {
   traffic_light_position_ = std::move(position);
   if (buttons_proxy_) {
@@ -1483,7 +1483,7 @@ void NativeWindowMac::SetTrafficLightPosition(
   }
 }
 
-absl::optional<gfx::Point> NativeWindowMac::GetTrafficLightPosition() const {
+absl::optional<gfx::Point> NativeWindowMac::GetWindowButtonPosition() const {
   return traffic_light_position_;
 }
 

+ 36 - 0
shell/common/gin_converters/optional_converter.h

@@ -0,0 +1,36 @@
+// Copyright (c) 2023 Microsoft, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef ELECTRON_SHELL_COMMON_GIN_CONVERTERS_OPTIONAL_CONVERTER_H_
+#define ELECTRON_SHELL_COMMON_GIN_CONVERTERS_OPTIONAL_CONVERTER_H_
+
+#include <utility>
+
+#include "gin/converter.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace gin {
+
+template <typename T>
+struct Converter<absl::optional<T>> {
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
+                                   const absl::optional<T>& val) {
+    if (val)
+      return Converter<T>::ToV8(isolate, val.value());
+    else
+      return v8::Null(isolate);
+  }
+  static bool FromV8(v8::Isolate* isolate,
+                     v8::Local<v8::Value> val,
+                     absl::optional<T>* out) {
+    T converted;
+    if (Converter<T>::FromV8(isolate, val, &converted))
+      out->emplace(std::move(converted));
+    return true;
+  }
+};
+
+}  // namespace gin
+
+#endif  // ELECTRON_SHELL_COMMON_GIN_CONVERTERS_OPTIONAL_CONVERTER_H_

+ 51 - 0
spec/api-browser-window-spec.ts

@@ -2087,7 +2087,52 @@ describe('BrowserWindow module', () => {
     const pos = { x: 10, y: 10 };
     afterEach(closeAllWindows);
 
+    describe('BrowserWindow.getWindowButtonPosition(pos)', () => {
+      it('returns null when there is no custom position', () => {
+        const w = new BrowserWindow({ show: false });
+        expect(w.getWindowButtonPosition()).to.be.null('getWindowButtonPosition');
+      });
+
+      it('gets position property for "hidden" titleBarStyle', () => {
+        const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos });
+        expect(w.getWindowButtonPosition()).to.deep.equal(pos);
+      });
+
+      it('gets position property for "customButtonsOnHover" titleBarStyle', () => {
+        const w = new BrowserWindow({ show: false, titleBarStyle: 'customButtonsOnHover', trafficLightPosition: pos });
+        expect(w.getWindowButtonPosition()).to.deep.equal(pos);
+      });
+    });
+
+    describe('BrowserWindow.setWindowButtonPosition(pos)', () => {
+      it('resets the position when null is passed', () => {
+        const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos });
+        w.setWindowButtonPosition(null);
+        expect(w.getWindowButtonPosition()).to.be.null('setWindowButtonPosition');
+      });
+
+      it('sets position property for "hidden" titleBarStyle', () => {
+        const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos });
+        const newPos = { x: 20, y: 20 };
+        w.setWindowButtonPosition(newPos);
+        expect(w.getWindowButtonPosition()).to.deep.equal(newPos);
+      });
+
+      it('sets position property for "customButtonsOnHover" titleBarStyle', () => {
+        const w = new BrowserWindow({ show: false, titleBarStyle: 'customButtonsOnHover', trafficLightPosition: pos });
+        const newPos = { x: 20, y: 20 };
+        w.setWindowButtonPosition(newPos);
+        expect(w.getWindowButtonPosition()).to.deep.equal(newPos);
+      });
+    });
+
+    // The set/getTrafficLightPosition APIs are deprecated.
     describe('BrowserWindow.getTrafficLightPosition(pos)', () => {
+      it('returns { x: 0, y: 0 } when there is no custom position', () => {
+        const w = new BrowserWindow({ show: false });
+        expect(w.getTrafficLightPosition()).to.deep.equal({ x: 0, y: 0 });
+      });
+
       it('gets position property for "hidden" titleBarStyle', () => {
         const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos });
         expect(w.getTrafficLightPosition()).to.deep.equal(pos);
@@ -2100,6 +2145,12 @@ describe('BrowserWindow module', () => {
     });
 
     describe('BrowserWindow.setTrafficLightPosition(pos)', () => {
+      it('resets the position when { x: 0, y: 0 } is passed', () => {
+        const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos });
+        w.setTrafficLightPosition({ x: 0, y: 0 });
+        expect(w.getTrafficLightPosition()).to.deep.equal({ x: 0, y: 0 });
+      });
+
       it('sets position property for "hidden" titleBarStyle', () => {
         const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos });
         const newPos = { x: 20, y: 20 };