Skip to content

Commit

Permalink
feat: add BrowserWindow.set/getWindowButtonPosition APIs (#37094)
Browse files Browse the repository at this point in the history
  • Loading branch information
zcbenz committed Feb 17, 2023
1 parent 0a5e634 commit 0de1012
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 26 deletions.
26 changes: 23 additions & 3 deletions docs/api/browser-window.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_

Expand Down
40 changes: 40 additions & 0 deletions docs/breaking-changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions filenames.gni
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
20 changes: 20 additions & 0 deletions lib/browser/api/base-window.ts
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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', {
Expand Down
24 changes: 9 additions & 15 deletions shell/browser/api/electron_api_base_window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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",
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions shell/browser/api/electron_api_base_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions shell/browser/native_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions shell/browser/native_window_mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions shell/browser/native_window_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1474,7 +1474,7 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) {
![window_ standardWindowButton:NSWindowCloseButton].hidden;
}

void NativeWindowMac::SetTrafficLightPosition(
void NativeWindowMac::SetWindowButtonPosition(
absl::optional<gfx::Point> position) {
traffic_light_position_ = std::move(position);
if (buttons_proxy_) {
Expand All @@ -1483,7 +1483,7 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) {
}
}

absl::optional<gfx::Point> NativeWindowMac::GetTrafficLightPosition() const {
absl::optional<gfx::Point> NativeWindowMac::GetWindowButtonPosition() const {
return traffic_light_position_;
}

Expand Down
36 changes: 36 additions & 0 deletions shell/common/gin_converters/optional_converter.h
Original file line number Diff line number Diff line change
@@ -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 changes: 51 additions & 0 deletions spec/api-browser-window-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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 };
Expand Down

0 comments on commit 0de1012

Please sign in to comment.