Browse Source

feat: Add BrowserWindow option to hide window in Mission Control (macOS) (#36390)

trop[bot] 2 years ago
parent
commit
ef164e7e8d

+ 11 - 0
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 `<title>` 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

+ 18 - 0
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)

+ 5 - 0
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);

+ 6 - 0
shell/browser/native_window.h

@@ -219,6 +219,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);

+ 2 - 0
shell/browser/native_window_mac.h

@@ -102,6 +102,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;

+ 14 - 1
shell/browser/native_window_mac.mm

@@ -280,6 +280,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
@@ -418,6 +421,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);
@@ -1159,9 +1164,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 {

+ 2 - 0
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";
 

+ 1 - 0
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[];

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

@@ -4774,6 +4774,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', () => {