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 <samuel.maddock@gmail.com> Co-authored-by: Samuel Maddock <samuel.maddock@gmail.com> Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
This commit is contained in:
parent
8b430c9d26
commit
15540975ff
9 changed files with 77 additions and 1 deletions
|
@ -192,6 +192,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||||
macOS. Default is `false`.
|
macOS. Default is `false`.
|
||||||
* `skipTaskbar` boolean (optional) _macOS_ _Windows_ - Whether to show the window in taskbar.
|
* `skipTaskbar` boolean (optional) _macOS_ _Windows_ - Whether to show the window in taskbar.
|
||||||
Default is `false`.
|
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`.
|
* `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.
|
* `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
|
* `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`.
|
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])`
|
#### `win.setAlwaysOnTop(flag[, level][, relativeLevel])`
|
||||||
|
|
||||||
* `flag` boolean
|
* `flag` boolean
|
||||||
|
|
|
@ -881,6 +881,16 @@ gfx::Point BaseWindow::GetTrafficLightPosition() const {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if BUILDFLAG(IS_MAC)
|
||||||
|
bool BaseWindow::IsHiddenInMissionControl() {
|
||||||
|
return window_->IsHiddenInMissionControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseWindow::SetHiddenInMissionControl(bool hidden) {
|
||||||
|
window_->SetHiddenInMissionControl(hidden);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void BaseWindow::SetTouchBar(
|
void BaseWindow::SetTouchBar(
|
||||||
std::vector<gin_helper::PersistentDictionary> items) {
|
std::vector<gin_helper::PersistentDictionary> items) {
|
||||||
window_->SetTouchBar(std::move(items));
|
window_->SetTouchBar(std::move(items));
|
||||||
|
@ -1256,6 +1266,14 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate,
|
||||||
.SetMethod("getTrafficLightPosition",
|
.SetMethod("getTrafficLightPosition",
|
||||||
&BaseWindow::GetTrafficLightPosition)
|
&BaseWindow::GetTrafficLightPosition)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if BUILDFLAG(IS_MAC)
|
||||||
|
.SetMethod("isHiddenInMissionControl",
|
||||||
|
&BaseWindow::IsHiddenInMissionControl)
|
||||||
|
.SetMethod("setHiddenInMissionControl",
|
||||||
|
&BaseWindow::SetHiddenInMissionControl)
|
||||||
|
#endif
|
||||||
|
|
||||||
.SetMethod("_setTouchBarItems", &BaseWindow::SetTouchBar)
|
.SetMethod("_setTouchBarItems", &BaseWindow::SetTouchBar)
|
||||||
.SetMethod("_refreshTouchBarItem", &BaseWindow::RefreshTouchBarItem)
|
.SetMethod("_refreshTouchBarItem", &BaseWindow::RefreshTouchBarItem)
|
||||||
.SetMethod("_setEscapeTouchBarItem", &BaseWindow::SetEscapeTouchBarItem)
|
.SetMethod("_setEscapeTouchBarItem", &BaseWindow::SetEscapeTouchBarItem)
|
||||||
|
|
|
@ -198,6 +198,11 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||||
gfx::Point GetTrafficLightPosition() const;
|
gfx::Point GetTrafficLightPosition() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if BUILDFLAG(IS_MAC)
|
||||||
|
bool IsHiddenInMissionControl();
|
||||||
|
void SetHiddenInMissionControl(bool hidden);
|
||||||
|
#endif
|
||||||
|
|
||||||
void SetTouchBar(std::vector<gin_helper::PersistentDictionary> items);
|
void SetTouchBar(std::vector<gin_helper::PersistentDictionary> items);
|
||||||
void RefreshTouchBarItem(const std::string& item_id);
|
void RefreshTouchBarItem(const std::string& item_id);
|
||||||
void SetEscapeTouchBarItem(gin_helper::PersistentDictionary item);
|
void SetEscapeTouchBarItem(gin_helper::PersistentDictionary item);
|
||||||
|
|
|
@ -227,6 +227,12 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
virtual void UpdateFrame() = 0;
|
virtual void UpdateFrame() = 0;
|
||||||
#endif
|
#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
|
// Touchbar API
|
||||||
virtual void SetTouchBar(std::vector<gin_helper::PersistentDictionary> items);
|
virtual void SetTouchBar(std::vector<gin_helper::PersistentDictionary> items);
|
||||||
virtual void RefreshTouchBarItem(const std::string& item_id);
|
virtual void RefreshTouchBarItem(const std::string& item_id);
|
||||||
|
|
|
@ -103,6 +103,8 @@ class NativeWindowMac : public NativeWindow,
|
||||||
void SetDocumentEdited(bool edited) override;
|
void SetDocumentEdited(bool edited) override;
|
||||||
bool IsDocumentEdited() override;
|
bool IsDocumentEdited() override;
|
||||||
void SetIgnoreMouseEvents(bool ignore, bool forward) override;
|
void SetIgnoreMouseEvents(bool ignore, bool forward) override;
|
||||||
|
bool IsHiddenInMissionControl() override;
|
||||||
|
void SetHiddenInMissionControl(bool hidden) override;
|
||||||
void SetContentProtection(bool enable) override;
|
void SetContentProtection(bool enable) override;
|
||||||
void SetFocusable(bool focusable) override;
|
void SetFocusable(bool focusable) override;
|
||||||
bool IsFocusable() override;
|
bool IsFocusable() override;
|
||||||
|
|
|
@ -209,6 +209,9 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||||
std::string windowType;
|
std::string windowType;
|
||||||
options.Get(options::kType, &windowType);
|
options.Get(options::kType, &windowType);
|
||||||
|
|
||||||
|
bool hiddenInMissionControl = false;
|
||||||
|
options.Get(options::kHiddenInMissionControl, &hiddenInMissionControl);
|
||||||
|
|
||||||
bool useStandardWindow = true;
|
bool useStandardWindow = true;
|
||||||
// eventually deprecate separate "standardWindow" option in favor of
|
// eventually deprecate separate "standardWindow" option in favor of
|
||||||
// standard / textured window types
|
// standard / textured window types
|
||||||
|
@ -347,6 +350,8 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||||
options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor);
|
options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor);
|
||||||
[window_ setDisableAutoHideCursor:disableAutoHideCursor];
|
[window_ setDisableAutoHideCursor:disableAutoHideCursor];
|
||||||
|
|
||||||
|
SetHiddenInMissionControl(hiddenInMissionControl);
|
||||||
|
|
||||||
// Set maximizable state last to ensure zoom button does not get reset
|
// Set maximizable state last to ensure zoom button does not get reset
|
||||||
// by calls to other APIs.
|
// by calls to other APIs.
|
||||||
SetMaximizable(maximizable);
|
SetMaximizable(maximizable);
|
||||||
|
@ -1085,9 +1090,17 @@ bool NativeWindowMac::IsDocumentEdited() {
|
||||||
return [window_ 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) {
|
void NativeWindowMac::SetIgnoreMouseEvents(bool ignore, bool forward) {
|
||||||
[window_ setIgnoresMouseEvents:ignore];
|
[window_ setIgnoresMouseEvents:ignore];
|
||||||
|
|
||||||
if (!ignore) {
|
if (!ignore) {
|
||||||
SetForwardMouseMessages(NO);
|
SetForwardMouseMessages(NO);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -39,6 +39,8 @@ const char kOverlaySymbolColor[] = "symbolColor";
|
||||||
// The custom height for Window Controls Overlay.
|
// The custom height for Window Controls Overlay.
|
||||||
const char kOverlayHeight[] = "height";
|
const char kOverlayHeight[] = "height";
|
||||||
|
|
||||||
|
// whether to keep the window out of mission control
|
||||||
|
const char kHiddenInMissionControl[] = "hiddenInMissionControl";
|
||||||
// Whether the window should show in taskbar.
|
// Whether the window should show in taskbar.
|
||||||
const char kSkipTaskbar[] = "skipTaskbar";
|
const char kSkipTaskbar[] = "skipTaskbar";
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ extern const char kMinimizable[];
|
||||||
extern const char kMaximizable[];
|
extern const char kMaximizable[];
|
||||||
extern const char kFullScreenable[];
|
extern const char kFullScreenable[];
|
||||||
extern const char kClosable[];
|
extern const char kClosable[];
|
||||||
|
extern const char kHiddenInMissionControl[];
|
||||||
extern const char kFullscreen[];
|
extern const char kFullscreen[];
|
||||||
extern const char kSkipTaskbar[];
|
extern const char kSkipTaskbar[];
|
||||||
extern const char kKiosk[];
|
extern const char kKiosk[];
|
||||||
|
|
|
@ -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
|
// fullscreen events are dispatched eagerly and twiddling things too fast can confuse poor Electron
|
||||||
|
|
||||||
ifdescribe(process.platform === 'darwin')('kiosk state', () => {
|
ifdescribe(process.platform === 'darwin')('kiosk state', () => {
|
||||||
|
|
Loading…
Reference in a new issue