From 3e2cec83d927b991855e21cc311ca9046e332601 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Thu, 5 Mar 2020 14:22:12 -0800 Subject: [PATCH] feat: programmatically modify traffic light positioning (#22533) * setter * getter * specs and docs * fixup Co-authored-by: Samuel Attard --- docs/api/browser-window.md | 11 ++++++ .../api/electron_api_top_level_window.cc | 16 +++++++++ .../api/electron_api_top_level_window.h | 6 ++++ shell/browser/native_window.h | 6 ++++ shell/browser/native_window_mac.h | 2 ++ shell/browser/native_window_mac.mm | 9 +++++ spec-main/api-browser-window-spec.ts | 35 ++++++++++++++++--- 7 files changed, 80 insertions(+), 5 deletions(-) diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 338d83ddf0db..4c59040d6fc6 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -1750,6 +1750,17 @@ 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_ + +* `position` [Point](structures/point.md) + +Set a custom position for the traffic light buttons. Can only be used with `titleBarStyle` set to `hidden`. + +#### `win.getTrafficLightPosition()` _macOS_ + +Returns `Point` - The current position for the traffic light buttons. Can only be used with `titleBarStyle` +set to `hidden`. + #### `win.setTouchBar(touchBar)` _macOS_ _Experimental_ * `touchBar` TouchBar | null diff --git a/shell/browser/api/electron_api_top_level_window.cc b/shell/browser/api/electron_api_top_level_window.cc index 53aaf4c2886f..a38b6a7fa29c 100644 --- a/shell/browser/api/electron_api_top_level_window.cc +++ b/shell/browser/api/electron_api_top_level_window.cc @@ -814,6 +814,16 @@ void TopLevelWindow::SetVibrancy(v8::Isolate* isolate, window_->SetVibrancy(type); } +#if defined(OS_MACOSX) +void TopLevelWindow::SetTrafficLightPosition(const gfx::Point& position) { + window_->SetTrafficLightPosition(position); +} + +gfx::Point TopLevelWindow::GetTrafficLightPosition() const { + return window_->GetTrafficLightPosition(); +} +#endif + void TopLevelWindow::SetTouchBar( std::vector items) { window_->SetTouchBar(std::move(items)); @@ -1184,6 +1194,12 @@ void TopLevelWindow::BuildPrototype(v8::Isolate* isolate, .SetMethod("setAutoHideCursor", &TopLevelWindow::SetAutoHideCursor) #endif .SetMethod("setVibrancy", &TopLevelWindow::SetVibrancy) +#if defined(OS_MACOSX) + .SetMethod("setTrafficLightPosition", + &TopLevelWindow::SetTrafficLightPosition) + .SetMethod("getTrafficLightPosition", + &TopLevelWindow::GetTrafficLightPosition) +#endif .SetMethod("_setTouchBarItems", &TopLevelWindow::SetTouchBar) .SetMethod("_refreshTouchBarItem", &TopLevelWindow::RefreshTouchBarItem) .SetMethod("_setEscapeTouchBarItem", diff --git a/shell/browser/api/electron_api_top_level_window.h b/shell/browser/api/electron_api_top_level_window.h index 615890333dd7..2becb245bd46 100644 --- a/shell/browser/api/electron_api_top_level_window.h +++ b/shell/browser/api/electron_api_top_level_window.h @@ -185,6 +185,12 @@ class TopLevelWindow : public gin_helper::TrackableObject, bool IsVisibleOnAllWorkspaces(); void SetAutoHideCursor(bool auto_hide); virtual void SetVibrancy(v8::Isolate* isolate, v8::Local value); + +#if defined(OS_MACOSX) + void SetTrafficLightPosition(const gfx::Point& position); + gfx::Point GetTrafficLightPosition() const; +#endif + void SetTouchBar(std::vector items); void RefreshTouchBarItem(const std::string& item_id); void SetEscapeTouchBarItem(gin_helper::PersistentDictionary item); diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index ff150d8a291e..8f6f5ade6148 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -194,6 +194,12 @@ class NativeWindow : public base::SupportsUserData, // Vibrancy API virtual void SetVibrancy(const std::string& type); + // Traffic Light API +#if defined(OS_MACOSX) + virtual void SetTrafficLightPosition(const gfx::Point& position) = 0; + virtual gfx::Point GetTrafficLightPosition() const = 0; +#endif + // Touchbar API virtual void SetTouchBar(std::vector items); virtual void RefreshTouchBarItem(const std::string& item_id); diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index c91bbe40d0eb..869908c9f5e2 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -153,6 +153,8 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { // Custom traffic light positioning void RepositionTrafficLights(); void SetExitingFullScreen(bool flag); + void SetTrafficLightPosition(const gfx::Point& position) override; + gfx::Point GetTrafficLightPosition() const override; void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override; enum class TitleBarStyle { diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 3f12030d2923..57d0350dc44e 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -1552,6 +1552,15 @@ void NativeWindowMac::SetVibrancy(const std::string& type) { [effect_view setMaterial:vibrancyType]; } +void NativeWindowMac::SetTrafficLightPosition(const gfx::Point& position) { + traffic_light_position_ = position; + RepositionTrafficLights(); +} + +gfx::Point NativeWindowMac::GetTrafficLightPosition() const { + return traffic_light_position_; +} + void NativeWindowMac::SetTouchBar( std::vector items) { if (@available(macOS 10.12.2, *)) { diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index 8155b78df672..7273271762fe 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -210,7 +210,7 @@ describe('BrowserWindow module', () => { }).to.throw('Object has been destroyed') }) it('should not crash when destroying windows with pending events', () => { - const focusListener = () => {} + const focusListener = () => { } app.on('browser-window-focus', focusListener) const windowCount = 3 const windowOptions = { @@ -281,7 +281,7 @@ describe('BrowserWindow module', () => { fs.readFile(filePath, (err, data) => { if (err) return if (parsedData.username === 'test' && - parsedData.file === data.toString()) { + parsedData.file === data.toString()) { res.end() } }) @@ -1347,6 +1347,31 @@ describe('BrowserWindow module', () => { }) }) + ifdescribe(process.platform === 'darwin')('BrowserWindow.getTrafficLightPosition(pos)', () => { + afterEach(closeAllWindows) + + it('gets the set traffic light position property', () => { + const pos = { x: 10, y: 10 } + const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos }) + const currentPosition = w.getTrafficLightPosition() + + expect(currentPosition).to.deep.equal(pos) + }) + }) + + ifdescribe(process.platform === 'darwin')('BrowserWindow.setTrafficLightPosition(pos)', () => { + afterEach(closeAllWindows) + + it('can set the traffic light position property', () => { + const pos = { x: 10, y: 10 } + const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos }) + w.setTrafficLightPosition(pos) + const currentPosition = w.getTrafficLightPosition() + + expect(currentPosition).to.deep.equal(pos) + }) + }) + ifdescribe(process.platform === 'win32')('BrowserWindow.setAppDetails(options)', () => { afterEach(closeAllWindows) @@ -1613,7 +1638,7 @@ describe('BrowserWindow module', () => { afterEach(closeAllWindows) describe('"preload" option', () => { - const doesNotLeakSpec = (name: string, webPrefs: {nodeIntegration: boolean, sandbox: boolean, contextIsolation: boolean}) => { + const doesNotLeakSpec = (name: string, webPrefs: { nodeIntegration: boolean, sandbox: boolean, contextIsolation: boolean }) => { it(name, async () => { const w = new BrowserWindow({ webPreferences: { @@ -1875,7 +1900,7 @@ describe('BrowserWindow module', () => { }) describe('"sandbox" option', () => { - function waitForEvents (emitter: {once: Function}, events: string[], callback: () => void) { + function waitForEvents (emitter: { once: Function }, events: string[], callback: () => void) { let count = events.length for (const event of events) { emitter.once(event, () => { @@ -2863,7 +2888,7 @@ describe('BrowserWindow module', () => { return } if (rect.height === contentHeight && rect.width === contentWidth && - !gotInitialFullSizeFrame) { + !gotInitialFullSizeFrame) { // The initial frame is full-size, but we're looking for a call // with just the dirty-rect. The next frame should be a smaller // rect.