feat: add BrowserWindow.set/getWindowButtonPosition APIs (#37094)
This commit is contained in:
		
					parent
					
						
							
								0a5e634736
							
						
					
				
			
			
				commit
				
					
						0de1012280
					
				
			
		
					 11 changed files with 188 additions and 26 deletions
				
			
		|  | @ -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. | ||||
| 
 | ||||
| #### `win.getTrafficLightPosition()` _macOS_ | ||||
| > **Note** | ||||
| > This function is deprecated. Use [setWindowButtonPosition](#winsetwindowbuttonpositionposition-macos) instead. | ||||
| 
 | ||||
| #### `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_ | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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", | ||||
|  |  | |||
|  | @ -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', { | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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( | ||||
|  |  | |||
|  | @ -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
									
								
								shell/common/gin_converters/optional_converter.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								shell/common/gin_converters/optional_converter.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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_
 | ||||
|  | @ -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 }; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Cheng Zhao
				Cheng Zhao