feat: enable window controls overlay on macOS (#29253)
* feat: enable windows control overlay on macOS * address review feedback * chore: address review feedback * Address review feedback * update doc per review * only enable WCO when titleBarStyle is overlay * Revert "only enable WCO when titleBarStyle is overlay" This reverts commit 1b58b5b1fcb8f091880a4e5d1f8855399c44afad. * Add new titleBarOverlay property to manage feature * spelling fix * Update docs/api/frameless-window.md Co-authored-by: Samuel Attard <sam@electronjs.org> * Update shell/browser/api/electron_api_browser_window.cc Co-authored-by: Samuel Attard <sam@electronjs.org> * update per review feedback Co-authored-by: Samuel Attard <sam@electronjs.org>
This commit is contained in:
parent
0fe2836151
commit
1f8a46c9c6
16 changed files with 223 additions and 0 deletions
|
@ -392,6 +392,10 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||||
contain the layout of the document—without requiring scrolling. Enabling
|
contain the layout of the document—without requiring scrolling. Enabling
|
||||||
this will cause the `preferred-size-changed` event to be emitted on the
|
this will cause the `preferred-size-changed` event to be emitted on the
|
||||||
`WebContents` when the preferred size changes. Default is `false`.
|
`WebContents` when the preferred size changes. Default is `false`.
|
||||||
|
* `titleBarOverlay` Boolean (optional) - On macOS, when using a frameless window in conjunction with
|
||||||
|
`win.setWindowButtonVisibility(true)` or using a `titleBarStyle` so that the traffic lights are visible,
|
||||||
|
this property enables the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and
|
||||||
|
[CSS Environment Variables][overlay-css-env-vars]. Default is `false`.
|
||||||
|
|
||||||
When setting minimum or maximum window size with `minWidth`/`maxWidth`/
|
When setting minimum or maximum window size with `minWidth`/`maxWidth`/
|
||||||
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
|
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
|
||||||
|
@ -1815,3 +1819,5 @@ removed in future Electron releases.
|
||||||
[window-levels]: https://developer.apple.com/documentation/appkit/nswindow/level
|
[window-levels]: https://developer.apple.com/documentation/appkit/nswindow/level
|
||||||
[chrome-content-scripts]: https://developer.chrome.com/extensions/content_scripts#execution-environment
|
[chrome-content-scripts]: https://developer.chrome.com/extensions/content_scripts#execution-environment
|
||||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||||
|
[overlay-javascript-apis]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md#javascript-apis
|
||||||
|
[overlay-css-env-vars]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md#css-environment-variables
|
||||||
|
|
|
@ -61,6 +61,21 @@ const win = new BrowserWindow({ titleBarStyle: 'customButtonsOnHover', frame: fa
|
||||||
win.show()
|
win.show()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Windows Control Overlay
|
||||||
|
|
||||||
|
On macOS, when using a frameless window in conjuction with `win.setWindowButtonVisibility(true)` or using one of the `titleBarStyle`s described above so
|
||||||
|
that the traffic lights are visible, you can access the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and
|
||||||
|
[CSS Environment Variables][overlay-css-env-vars] by setting the `titleBarOverlay` option to true:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const { BrowserWindow } = require('electron')
|
||||||
|
const win = new BrowserWindow({
|
||||||
|
titleBarStyle: 'hiddenInset',
|
||||||
|
titleBarOverlay: true
|
||||||
|
})
|
||||||
|
win.show()
|
||||||
|
```
|
||||||
|
|
||||||
## Transparent window
|
## Transparent window
|
||||||
|
|
||||||
By setting the `transparent` option to `true`, you can also make the frameless
|
By setting the `transparent` option to `true`, you can also make the frameless
|
||||||
|
@ -186,3 +201,5 @@ behave correctly on all platforms you should never use a custom context menu on
|
||||||
draggable areas.
|
draggable areas.
|
||||||
|
|
||||||
[ignore-mouse-events]: browser-window.md#winsetignoremouseeventsignore-options
|
[ignore-mouse-events]: browser-window.md#winsetignoremouseeventsignore-options
|
||||||
|
[overlay-javascript-apis]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md#javascript-apis
|
||||||
|
[overlay-css-env-vars]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md#css-environment-variables
|
||||||
|
|
|
@ -57,6 +57,17 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
|
||||||
web_preferences.Set(options::kShow, show);
|
web_preferences.Set(options::kShow, show);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool titleBarOverlay = false;
|
||||||
|
options.Get(options::ktitleBarOverlay, &titleBarOverlay);
|
||||||
|
if (titleBarOverlay) {
|
||||||
|
std::string enabled_features = "";
|
||||||
|
if (web_preferences.Get(options::kEnableBlinkFeatures, &enabled_features)) {
|
||||||
|
enabled_features += ",";
|
||||||
|
}
|
||||||
|
enabled_features += features::kWebAppWindowControlsOverlay.name;
|
||||||
|
web_preferences.Set(options::kEnableBlinkFeatures, enabled_features);
|
||||||
|
}
|
||||||
|
|
||||||
// Copy the webContents option to webPreferences. This is only used internally
|
// Copy the webContents option to webPreferences. This is only used internally
|
||||||
// to implement nativeWindowOpen option.
|
// to implement nativeWindowOpen option.
|
||||||
if (options.Get("webContents", &value)) {
|
if (options.Get("webContents", &value)) {
|
||||||
|
@ -312,6 +323,11 @@ void BrowserWindow::OnWindowLeaveFullScreen() {
|
||||||
BaseWindow::OnWindowLeaveFullScreen();
|
BaseWindow::OnWindowLeaveFullScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BrowserWindow::UpdateWindowControlsOverlay(
|
||||||
|
const gfx::Rect& bounding_rect) {
|
||||||
|
web_contents()->UpdateWindowControlsOverlay(bounding_rect);
|
||||||
|
}
|
||||||
|
|
||||||
void BrowserWindow::CloseImmediately() {
|
void BrowserWindow::CloseImmediately() {
|
||||||
// Close all child windows before closing current window.
|
// Close all child windows before closing current window.
|
||||||
v8::Locker locker(isolate());
|
v8::Locker locker(isolate());
|
||||||
|
|
|
@ -69,6 +69,7 @@ class BrowserWindow : public BaseWindow,
|
||||||
void RequestPreferredWidth(int* width) override;
|
void RequestPreferredWidth(int* width) override;
|
||||||
void OnCloseButtonClicked(bool* prevent_default) override;
|
void OnCloseButtonClicked(bool* prevent_default) override;
|
||||||
void OnWindowIsKeyChanged(bool is_key) override;
|
void OnWindowIsKeyChanged(bool is_key) override;
|
||||||
|
void UpdateWindowControlsOverlay(const gfx::Rect& bounding_rect) override;
|
||||||
|
|
||||||
// BaseWindow:
|
// BaseWindow:
|
||||||
void OnWindowBlur() override;
|
void OnWindowBlur() override;
|
||||||
|
|
|
@ -1675,6 +1675,10 @@ void WebContents::ReadyToCommitNavigation(
|
||||||
|
|
||||||
void WebContents::DidFinishNavigation(
|
void WebContents::DidFinishNavigation(
|
||||||
content::NavigationHandle* navigation_handle) {
|
content::NavigationHandle* navigation_handle) {
|
||||||
|
if (owner_window_) {
|
||||||
|
owner_window_->NotifyLayoutWindowControlsOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
if (!navigation_handle->HasCommitted())
|
if (!navigation_handle->HasCommitted())
|
||||||
return;
|
return;
|
||||||
bool is_main_frame = navigation_handle->IsInMainFrame();
|
bool is_main_frame = navigation_handle->IsInMainFrame();
|
||||||
|
|
|
@ -53,6 +53,7 @@ NativeWindow::NativeWindow(const gin_helper::Dictionary& options,
|
||||||
options.Get(options::kFrame, &has_frame_);
|
options.Get(options::kFrame, &has_frame_);
|
||||||
options.Get(options::kTransparent, &transparent_);
|
options.Get(options::kTransparent, &transparent_);
|
||||||
options.Get(options::kEnableLargerThanScreen, &enable_larger_than_screen_);
|
options.Get(options::kEnableLargerThanScreen, &enable_larger_than_screen_);
|
||||||
|
options.Get(options::ktitleBarOverlay, &titlebar_overlay_);
|
||||||
|
|
||||||
if (parent)
|
if (parent)
|
||||||
options.Get("modal", &is_modal_);
|
options.Get("modal", &is_modal_);
|
||||||
|
@ -394,6 +395,14 @@ void NativeWindow::PreviewFile(const std::string& path,
|
||||||
|
|
||||||
void NativeWindow::CloseFilePreview() {}
|
void NativeWindow::CloseFilePreview() {}
|
||||||
|
|
||||||
|
gfx::Rect NativeWindow::GetWindowControlsOverlayRect() {
|
||||||
|
return overlay_rect_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeWindow::SetWindowControlsOverlayRect(const gfx::Rect& overlay_rect) {
|
||||||
|
overlay_rect_ = overlay_rect;
|
||||||
|
}
|
||||||
|
|
||||||
void NativeWindow::NotifyWindowRequestPreferredWith(int* width) {
|
void NativeWindow::NotifyWindowRequestPreferredWith(int* width) {
|
||||||
for (NativeWindowObserver& observer : observers_)
|
for (NativeWindowObserver& observer : observers_)
|
||||||
observer.RequestPreferredWidth(width);
|
observer.RequestPreferredWidth(width);
|
||||||
|
@ -493,6 +502,7 @@ void NativeWindow::NotifyWindowWillMove(const gfx::Rect& new_bounds,
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeWindow::NotifyWindowResize() {
|
void NativeWindow::NotifyWindowResize() {
|
||||||
|
NotifyLayoutWindowControlsOverlay();
|
||||||
for (NativeWindowObserver& observer : observers_)
|
for (NativeWindowObserver& observer : observers_)
|
||||||
observer.OnWindowResize();
|
observer.OnWindowResize();
|
||||||
}
|
}
|
||||||
|
@ -591,6 +601,14 @@ void NativeWindow::NotifyWindowSystemContextMenu(int x,
|
||||||
observer.OnSystemContextMenu(x, y, prevent_default);
|
observer.OnSystemContextMenu(x, y, prevent_default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NativeWindow::NotifyLayoutWindowControlsOverlay() {
|
||||||
|
gfx::Rect bounding_rect = GetWindowControlsOverlayRect();
|
||||||
|
if (!bounding_rect.IsEmpty()) {
|
||||||
|
for (NativeWindowObserver& observer : observers_)
|
||||||
|
observer.UpdateWindowControlsOverlay(bounding_rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
void NativeWindow::NotifyWindowMessage(UINT message,
|
void NativeWindow::NotifyWindowMessage(UINT message,
|
||||||
WPARAM w_param,
|
WPARAM w_param,
|
||||||
|
|
|
@ -255,6 +255,9 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
return weak_factory_.GetWeakPtr();
|
return weak_factory_.GetWeakPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual gfx::Rect GetWindowControlsOverlayRect();
|
||||||
|
virtual void SetWindowControlsOverlayRect(const gfx::Rect& overlay_rect);
|
||||||
|
|
||||||
// Methods called by the WebContents.
|
// Methods called by the WebContents.
|
||||||
virtual void HandleKeyboardEvent(
|
virtual void HandleKeyboardEvent(
|
||||||
content::WebContents*,
|
content::WebContents*,
|
||||||
|
@ -299,6 +302,7 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
const base::DictionaryValue& details);
|
const base::DictionaryValue& details);
|
||||||
void NotifyNewWindowForTab();
|
void NotifyNewWindowForTab();
|
||||||
void NotifyWindowSystemContextMenu(int x, int y, bool* prevent_default);
|
void NotifyWindowSystemContextMenu(int x, int y, bool* prevent_default);
|
||||||
|
void NotifyLayoutWindowControlsOverlay();
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
void NotifyWindowMessage(UINT message, WPARAM w_param, LPARAM l_param);
|
void NotifyWindowMessage(UINT message, WPARAM w_param, LPARAM l_param);
|
||||||
|
@ -343,6 +347,8 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
[&browser_view](NativeBrowserView* n) { return (n == browser_view); });
|
[&browser_view](NativeBrowserView* n) { return (n == browser_view); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool titlebar_overlay_ = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<views::Widget> widget_;
|
std::unique_ptr<views::Widget> widget_;
|
||||||
|
|
||||||
|
@ -391,6 +397,8 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
// Accessible title.
|
// Accessible title.
|
||||||
std::u16string accessible_title_;
|
std::u16string accessible_title_;
|
||||||
|
|
||||||
|
gfx::Rect overlay_rect_;
|
||||||
|
|
||||||
base::WeakPtrFactory<NativeWindow> weak_factory_{this};
|
base::WeakPtrFactory<NativeWindow> weak_factory_{this};
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(NativeWindow);
|
DISALLOW_COPY_AND_ASSIGN(NativeWindow);
|
||||||
|
|
|
@ -147,6 +147,7 @@ class NativeWindowMac : public NativeWindow,
|
||||||
void CloseFilePreview() override;
|
void CloseFilePreview() override;
|
||||||
gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) const override;
|
gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) const override;
|
||||||
gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) const override;
|
gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) const override;
|
||||||
|
gfx::Rect GetWindowControlsOverlayRect() override;
|
||||||
void NotifyWindowEnterFullScreen() override;
|
void NotifyWindowEnterFullScreen() override;
|
||||||
void NotifyWindowLeaveFullScreen() override;
|
void NotifyWindowLeaveFullScreen() override;
|
||||||
void SetActive(bool is_key) override;
|
void SetActive(bool is_key) override;
|
||||||
|
|
|
@ -1488,6 +1488,7 @@ void NativeWindowMac::SetVibrancy(const std::string& type) {
|
||||||
void NativeWindowMac::SetWindowButtonVisibility(bool visible) {
|
void NativeWindowMac::SetWindowButtonVisibility(bool visible) {
|
||||||
window_button_visibility_ = visible;
|
window_button_visibility_ = visible;
|
||||||
InternalSetWindowButtonVisibility(visible);
|
InternalSetWindowButtonVisibility(visible);
|
||||||
|
NotifyLayoutWindowControlsOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NativeWindowMac::GetWindowButtonVisibility() const {
|
bool NativeWindowMac::GetWindowButtonVisibility() const {
|
||||||
|
@ -1505,6 +1506,7 @@ void NativeWindowMac::SetTrafficLightPosition(
|
||||||
if (buttons_view_) {
|
if (buttons_view_) {
|
||||||
[buttons_view_ setMargin:traffic_light_position_];
|
[buttons_view_ setMargin:traffic_light_position_];
|
||||||
[buttons_view_ viewDidMoveToWindow];
|
[buttons_view_ viewDidMoveToWindow];
|
||||||
|
NotifyLayoutWindowControlsOverlay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1859,6 +1861,27 @@ void NativeWindowMac::SetForwardMouseMessages(bool forward) {
|
||||||
[window_ setAcceptsMouseMovedEvents:forward];
|
[window_ setAcceptsMouseMovedEvents:forward];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gfx::Rect NativeWindowMac::GetWindowControlsOverlayRect() {
|
||||||
|
gfx::Rect bounding_rect;
|
||||||
|
if (titlebar_overlay_ && !has_frame() && buttons_view_ &&
|
||||||
|
![buttons_view_ isHidden]) {
|
||||||
|
NSRect button_frame = [buttons_view_ frame];
|
||||||
|
gfx::Point buttons_view_margin = [buttons_view_ getMargin];
|
||||||
|
const int overlay_width = GetContentSize().width() - NSWidth(button_frame) -
|
||||||
|
buttons_view_margin.x();
|
||||||
|
CGFloat overlay_height =
|
||||||
|
NSHeight(button_frame) + buttons_view_margin.y() * 2;
|
||||||
|
if (base::i18n::IsRTL()) {
|
||||||
|
bounding_rect = gfx::Rect(0, 0, overlay_width, overlay_height);
|
||||||
|
} else {
|
||||||
|
bounding_rect =
|
||||||
|
gfx::Rect(button_frame.size.width + buttons_view_margin.x(), 0,
|
||||||
|
overlay_width, overlay_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bounding_rect;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
NativeWindow* NativeWindow::Create(const gin_helper::Dictionary& options,
|
NativeWindow* NativeWindow::Create(const gin_helper::Dictionary& options,
|
||||||
NativeWindow* parent) {
|
NativeWindow* parent) {
|
||||||
|
|
|
@ -104,6 +104,8 @@ class NativeWindowObserver : public base::CheckedObserver {
|
||||||
// Called on Windows when App Commands arrive (WM_APPCOMMAND)
|
// Called on Windows when App Commands arrive (WM_APPCOMMAND)
|
||||||
// Some commands are implemented on on other platforms as well
|
// Some commands are implemented on on other platforms as well
|
||||||
virtual void OnExecuteAppCommand(const std::string& command_name) {}
|
virtual void OnExecuteAppCommand(const std::string& command_name) {}
|
||||||
|
|
||||||
|
virtual void UpdateWindowControlsOverlay(const gfx::Rect& bounding_rect) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace electron
|
} // namespace electron
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
- (void)setMargin:(const absl::optional<gfx::Point>&)margin;
|
- (void)setMargin:(const absl::optional<gfx::Point>&)margin;
|
||||||
- (void)setShowOnHover:(BOOL)yes;
|
- (void)setShowOnHover:(BOOL)yes;
|
||||||
- (void)setNeedsDisplayForButtons;
|
- (void)setNeedsDisplayForButtons;
|
||||||
|
- (gfx::Point)getMargin;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#endif // SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_VIEW_H_
|
#endif // SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_VIEW_H_
|
||||||
|
|
|
@ -116,4 +116,8 @@ const NSWindowButton kButtonTypes[] = {
|
||||||
[self setNeedsDisplayForButtons];
|
[self setNeedsDisplayForButtons];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (gfx::Point)getMargin {
|
||||||
|
return margin_;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -194,6 +194,8 @@ const char kEnableWebSQL[] = "enableWebSQL";
|
||||||
|
|
||||||
const char kEnablePreferredSizeMode[] = "enablePreferredSizeMode";
|
const char kEnablePreferredSizeMode[] = "enablePreferredSizeMode";
|
||||||
|
|
||||||
|
const char ktitleBarOverlay[] = "titleBarOverlay";
|
||||||
|
|
||||||
} // namespace options
|
} // namespace options
|
||||||
|
|
||||||
namespace switches {
|
namespace switches {
|
||||||
|
|
|
@ -57,6 +57,7 @@ extern const char kVibrancyType[];
|
||||||
extern const char kVisualEffectState[];
|
extern const char kVisualEffectState[];
|
||||||
extern const char kTrafficLightPosition[];
|
extern const char kTrafficLightPosition[];
|
||||||
extern const char kRoundedCorners[];
|
extern const char kRoundedCorners[];
|
||||||
|
extern const char ktitleBarOverlay[];
|
||||||
|
|
||||||
// WebPreferences.
|
// WebPreferences.
|
||||||
extern const char kZoomFactor[];
|
extern const char kZoomFactor[];
|
||||||
|
|
|
@ -1876,7 +1876,36 @@ describe('BrowserWindow module', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
ifdescribe(process.platform === 'darwin' && parseInt(os.release().split('.')[0]) >= 14)('"titleBarStyle" option', () => {
|
ifdescribe(process.platform === 'darwin' && parseInt(os.release().split('.')[0]) >= 14)('"titleBarStyle" option', () => {
|
||||||
|
const testWindowsOverlay = async (style: any) => {
|
||||||
|
const w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
width: 400,
|
||||||
|
height: 400,
|
||||||
|
titleBarStyle: style,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false
|
||||||
|
},
|
||||||
|
titleBarOverlay: true
|
||||||
|
});
|
||||||
|
const overlayHTML = path.join(__dirname, 'fixtures', 'pages', 'overlay.html');
|
||||||
|
await w.loadFile(overlayHTML);
|
||||||
|
const overlayEnabled = await w.webContents.executeJavaScript('navigator.windowControlsOverlay.visible');
|
||||||
|
expect(overlayEnabled).to.be.true('overlayEnabled');
|
||||||
|
const overlayRect = await w.webContents.executeJavaScript('getJSOverlayProperties()');
|
||||||
|
expect(overlayRect.y).to.equal(0);
|
||||||
|
expect(overlayRect.x).to.be.greaterThan(0);
|
||||||
|
expect(overlayRect.width).to.be.greaterThan(0);
|
||||||
|
expect(overlayRect.height).to.be.greaterThan(0);
|
||||||
|
const cssOverlayRect = await w.webContents.executeJavaScript('getCssOverlayProperties();');
|
||||||
|
expect(cssOverlayRect).to.deep.equal(overlayRect);
|
||||||
|
const geometryChange = emittedOnce(ipcMain, 'geometrychange');
|
||||||
|
w.setBounds({ width: 800 });
|
||||||
|
const [, newOverlayRect] = await geometryChange;
|
||||||
|
expect(newOverlayRect.width).to.equal(overlayRect.width + 400);
|
||||||
|
};
|
||||||
afterEach(closeAllWindows);
|
afterEach(closeAllWindows);
|
||||||
|
afterEach(() => { ipcMain.removeAllListeners('geometrychange'); });
|
||||||
it('creates browser window with hidden title bar', () => {
|
it('creates browser window with hidden title bar', () => {
|
||||||
const w = new BrowserWindow({
|
const w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
|
@ -1897,6 +1926,12 @@ describe('BrowserWindow module', () => {
|
||||||
const contentSize = w.getContentSize();
|
const contentSize = w.getContentSize();
|
||||||
expect(contentSize).to.deep.equal([400, 400]);
|
expect(contentSize).to.deep.equal([400, 400]);
|
||||||
});
|
});
|
||||||
|
it('sets Window Control Overlay with hidden title bar', async () => {
|
||||||
|
await testWindowsOverlay('hidden');
|
||||||
|
});
|
||||||
|
it('sets Window Control Overlay with hidden inset title bar', async () => {
|
||||||
|
await testWindowsOverlay('hiddenInset');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ifdescribe(process.platform === 'darwin')('"enableLargerThanScreen" option', () => {
|
ifdescribe(process.platform === 'darwin')('"enableLargerThanScreen" option', () => {
|
||||||
|
|
84
spec-main/fixtures/pages/overlay.html
Normal file
84
spec-main/fixtures/pages/overlay.html
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--fallback-title-bar-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable {
|
||||||
|
app-region: drag;
|
||||||
|
/* Pre-fix app-region during standardization process */
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nonDraggable {
|
||||||
|
app-region: no-drag;
|
||||||
|
/* Pre-fix app-region during standardization process */
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#titleBarContainer {
|
||||||
|
position: absolute;
|
||||||
|
top: env(titlebar-area-y, 0);
|
||||||
|
height: env(titlebar-area-height, var(--fallback-title-bar-height));
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#titleBar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
display: flex;
|
||||||
|
user-select: none;
|
||||||
|
height: 100%;
|
||||||
|
left: env(titlebar-area-x, 0);
|
||||||
|
width: env(titlebar-area-width, 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainContent {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
top: env(titlebar-area-height, var(--fallback-title-bar-height));
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
const {ipcRenderer} = require('electron');
|
||||||
|
navigator.windowControlsOverlay.ongeometrychange = function() {
|
||||||
|
const {x, y, width, height} = navigator.windowControlsOverlay.getBoundingClientRect();
|
||||||
|
ipcRenderer.send('geometrychange', {x, y, width, height});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<div id="titleBarContainer">
|
||||||
|
<div id="titleBar" class=" draggable">
|
||||||
|
<span class="draggable">Title goes here</span>
|
||||||
|
<input class="nonDraggable" type="text" placeholder="Search"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="mainContent"><!-- The rest of the webpage --></div>
|
||||||
|
<script>
|
||||||
|
function getCssOverlayProperties() {
|
||||||
|
const cssOverlayProps = {};
|
||||||
|
const titleBarContainer = document.getElementById('titleBarContainer');
|
||||||
|
const titleBar = document.getElementById('titleBar');
|
||||||
|
cssOverlayProps.y = titleBarContainer.computedStyleMap().get('top').value;
|
||||||
|
cssOverlayProps.height = titleBarContainer.computedStyleMap().get('height').value;
|
||||||
|
cssOverlayProps.x = titleBar.computedStyleMap().get('left').value;
|
||||||
|
cssOverlayProps.width = titleBar.computedStyleMap().get('width').value;
|
||||||
|
return cssOverlayProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getJSOverlayProperties() {
|
||||||
|
const {x, y, width, height} = navigator.windowControlsOverlay.getBoundingClientRect();
|
||||||
|
return {x, y, width, height};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue