From 15540975ff3f3979ba93ed560fdb18489c458b98 Mon Sep 17 00:00:00 2001 From: Brad Carter <16466430+carterbs@users.noreply.github.com> Date: Tue, 1 Nov 2022 16:43:42 -0400 Subject: [PATCH] feat: Add BrowserWindow option to hide window in Mission Control (macOS) (#36092) * feat: Add BrowserWindow option to ignore Mission Control (macOS) * There are many circumstances when app developers may want to hide their windows from mission control. E.g., full screen overlays, small helper windows, dialogs, etc. * This PR adds the functionality, docs, and tests. * chore:Rename variables * Update shell/browser/native_window_mac.h Co-authored-by: Samuel Maddock Co-authored-by: Samuel Maddock Co-authored-by: John Kleinschmidt --- docs/api/browser-window.md | 11 +++++++++++ shell/browser/api/electron_api_base_window.cc | 18 ++++++++++++++++++ shell/browser/api/electron_api_base_window.h | 5 +++++ shell/browser/native_window.h | 6 ++++++ shell/browser/native_window_mac.h | 2 ++ shell/browser/native_window_mac.mm | 15 ++++++++++++++- shell/common/options_switches.cc | 2 ++ shell/common/options_switches.h | 1 + spec/api-browser-window-spec.ts | 18 ++++++++++++++++++ 9 files changed, 77 insertions(+), 1 deletion(-) diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 5f21805d8717..d2f3a35991ef 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -192,6 +192,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. macOS. Default is `false`. * `skipTaskbar` boolean (optional) _macOS_ _Windows_ - Whether to show the window in taskbar. Default is `false`. + * `hiddenInMissionControl` boolean (optional) _macOS_ - Whether window should be hidden when the user toggles into mission control. * `kiosk` boolean (optional) - Whether the window is in kiosk mode. Default is `false`. * `title` string (optional) - Default window title. Default is `"Electron"`. If the HTML tag `` is defined in the HTML file loaded by `loadURL()`, this property will be ignored. * `icon` ([NativeImage](native-image.md) | string) (optional) - The window icon. On Windows it is @@ -1255,6 +1256,16 @@ Returns `boolean` - Whether the window can be manually closed by user. On Linux always returns `true`. +#### `win.setHiddenInMissionControl(hidden)` _macOS_ + +* `hidden` boolean + +Sets whether the window will be hidden when the user toggles into mission control. + +#### `win.isHiddenInMissionControl()` _macOS_ + +Returns `boolean` - Whether the window will be hidden when the user toggles into mission control. + #### `win.setAlwaysOnTop(flag[, level][, relativeLevel])` * `flag` boolean diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index 6aa498923e94..6ecc2684d106 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -881,6 +881,16 @@ gfx::Point BaseWindow::GetTrafficLightPosition() const { } #endif +#if BUILDFLAG(IS_MAC) +bool BaseWindow::IsHiddenInMissionControl() { + return window_->IsHiddenInMissionControl(); +} + +void BaseWindow::SetHiddenInMissionControl(bool hidden) { + window_->SetHiddenInMissionControl(hidden); +} +#endif + void BaseWindow::SetTouchBar( std::vector<gin_helper::PersistentDictionary> items) { window_->SetTouchBar(std::move(items)); @@ -1256,6 +1266,14 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate, .SetMethod("getTrafficLightPosition", &BaseWindow::GetTrafficLightPosition) #endif + +#if BUILDFLAG(IS_MAC) + .SetMethod("isHiddenInMissionControl", + &BaseWindow::IsHiddenInMissionControl) + .SetMethod("setHiddenInMissionControl", + &BaseWindow::SetHiddenInMissionControl) +#endif + .SetMethod("_setTouchBarItems", &BaseWindow::SetTouchBar) .SetMethod("_refreshTouchBarItem", &BaseWindow::RefreshTouchBarItem) .SetMethod("_setEscapeTouchBarItem", &BaseWindow::SetEscapeTouchBarItem) diff --git a/shell/browser/api/electron_api_base_window.h b/shell/browser/api/electron_api_base_window.h index 5500006939b9..9552a13a5983 100644 --- a/shell/browser/api/electron_api_base_window.h +++ b/shell/browser/api/electron_api_base_window.h @@ -198,6 +198,11 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>, gfx::Point GetTrafficLightPosition() const; #endif +#if BUILDFLAG(IS_MAC) + bool IsHiddenInMissionControl(); + void SetHiddenInMissionControl(bool hidden); +#endif + void SetTouchBar(std::vector<gin_helper::PersistentDictionary> 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 fcf1d2d6bd28..6eccbf2a9805 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -227,6 +227,12 @@ class NativeWindow : public base::SupportsUserData, virtual void UpdateFrame() = 0; #endif +// whether windows should be ignored by mission control +#if BUILDFLAG(IS_MAC) + virtual bool IsHiddenInMissionControl() = 0; + virtual void SetHiddenInMissionControl(bool hidden) = 0; +#endif + // Touchbar API virtual void SetTouchBar(std::vector<gin_helper::PersistentDictionary> 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 1e436cebad02..2d6722bad73b 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -103,6 +103,8 @@ class NativeWindowMac : public NativeWindow, void SetDocumentEdited(bool edited) override; bool IsDocumentEdited() override; void SetIgnoreMouseEvents(bool ignore, bool forward) override; + bool IsHiddenInMissionControl() override; + void SetHiddenInMissionControl(bool hidden) override; void SetContentProtection(bool enable) override; void SetFocusable(bool focusable) override; bool IsFocusable() override; diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index e2252b50eb88..afd3add3ae59 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -209,6 +209,9 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options, std::string windowType; options.Get(options::kType, &windowType); + bool hiddenInMissionControl = false; + options.Get(options::kHiddenInMissionControl, &hiddenInMissionControl); + bool useStandardWindow = true; // eventually deprecate separate "standardWindow" option in favor of // standard / textured window types @@ -347,6 +350,8 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options, options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor); [window_ setDisableAutoHideCursor:disableAutoHideCursor]; + SetHiddenInMissionControl(hiddenInMissionControl); + // Set maximizable state last to ensure zoom button does not get reset // by calls to other APIs. SetMaximizable(maximizable); @@ -1085,9 +1090,17 @@ bool NativeWindowMac::IsDocumentEdited() { return [window_ isDocumentEdited]; } +bool NativeWindowMac::IsHiddenInMissionControl() { + NSUInteger collectionBehavior = [window_ collectionBehavior]; + return collectionBehavior & NSWindowCollectionBehaviorTransient; +} + +void NativeWindowMac::SetHiddenInMissionControl(bool hidden) { + SetCollectionBehavior(hidden, NSWindowCollectionBehaviorTransient); +} + void NativeWindowMac::SetIgnoreMouseEvents(bool ignore, bool forward) { [window_ setIgnoresMouseEvents:ignore]; - if (!ignore) { SetForwardMouseMessages(NO); } else { diff --git a/shell/common/options_switches.cc b/shell/common/options_switches.cc index 4a6bef3a694f..a50ea98c1e78 100644 --- a/shell/common/options_switches.cc +++ b/shell/common/options_switches.cc @@ -39,6 +39,8 @@ const char kOverlaySymbolColor[] = "symbolColor"; // The custom height for Window Controls Overlay. const char kOverlayHeight[] = "height"; +// whether to keep the window out of mission control +const char kHiddenInMissionControl[] = "hiddenInMissionControl"; // Whether the window should show in taskbar. const char kSkipTaskbar[] = "skipTaskbar"; diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index 6c86d56799ac..46f966f879c5 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -30,6 +30,7 @@ extern const char kMinimizable[]; extern const char kMaximizable[]; extern const char kFullScreenable[]; extern const char kClosable[]; +extern const char kHiddenInMissionControl[]; extern const char kFullscreen[]; extern const char kSkipTaskbar[]; extern const char kKiosk[]; diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index 85fc5f229a49..68bb96c614c4 100644 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -4784,6 +4784,24 @@ describe('BrowserWindow module', () => { }); }); + ifdescribe(process.platform === 'darwin')('isHiddenInMissionControl state', () => { + it('with functions', () => { + it('can be set with ignoreMissionControl constructor option', () => { + const w = new BrowserWindow({ show: false, hiddenInMissionControl: true }); + expect(w.isHiddenInMissionControl()).to.be.true('isHiddenInMissionControl'); + }); + + it('can be changed', () => { + const w = new BrowserWindow({ show: false }); + expect(w.isHiddenInMissionControl()).to.be.false('isHiddenInMissionControl'); + w.setHiddenInMissionControl(true); + expect(w.isHiddenInMissionControl()).to.be.true('isHiddenInMissionControl'); + w.setHiddenInMissionControl(false); + expect(w.isHiddenInMissionControl()).to.be.false('isHiddenInMissionControl'); + }); + }); + }); + // fullscreen events are dispatched eagerly and twiddling things too fast can confuse poor Electron ifdescribe(process.platform === 'darwin')('kiosk state', () => {