From fafe6a4ddc22b1243cb956d687d4d692f8f23db6 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Wed, 23 Jun 2021 08:09:09 +0200 Subject: [PATCH] fix: child window alwaysOnTop level persistence --- shell/browser/api/electron_api_base_window.cc | 5 +++ shell/browser/api/electron_api_base_window.h | 1 + shell/browser/native_window.h | 1 + shell/browser/native_window_mac.h | 1 + shell/browser/native_window_mac.mm | 32 ++++++++++++++++++- spec-main/api-browser-window-spec.ts | 17 +++++++--- 6 files changed, 51 insertions(+), 6 deletions(-) diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index 402fa6accdc83..7415cea54e3dd 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -867,6 +867,10 @@ void BaseWindow::SetVibrancy(v8::Isolate* isolate, v8::Local value) { } #if defined(OS_MAC) +std::string BaseWindow::GetAlwaysOnTopLevel() { + return window_->GetAlwaysOnTopLevel(); +} + void BaseWindow::SetWindowButtonVisibility(bool visible) { window_->SetWindowButtonVisibility(visible); } @@ -1253,6 +1257,7 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate, .SetMethod("isVisibleOnAllWorkspaces", &BaseWindow::IsVisibleOnAllWorkspaces) #if defined(OS_MAC) + .SetMethod("_getAlwaysOnTopLevel", &BaseWindow::GetAlwaysOnTopLevel) .SetMethod("setAutoHideCursor", &BaseWindow::SetAutoHideCursor) #endif .SetMethod("setVibrancy", &BaseWindow::SetVibrancy) diff --git a/shell/browser/api/electron_api_base_window.h b/shell/browser/api/electron_api_base_window.h index 02a2e258dc64b..c6159af24e3ea 100644 --- a/shell/browser/api/electron_api_base_window.h +++ b/shell/browser/api/electron_api_base_window.h @@ -191,6 +191,7 @@ class BaseWindow : public gin_helper::TrackableObject, virtual void SetVibrancy(v8::Isolate* isolate, v8::Local value); #if defined(OS_MAC) + std::string GetAlwaysOnTopLevel(); void SetWindowButtonVisibility(bool visible); bool GetWindowButtonVisibility() const; void SetTrafficLightPosition(const gfx::Point& position); diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 6b808ec1c6aa6..6314cc4318401 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -201,6 +201,7 @@ class NativeWindow : public base::SupportsUserData, // Traffic Light API #if defined(OS_MAC) + virtual std::string GetAlwaysOnTopLevel() = 0; virtual void SetWindowButtonVisibility(bool visible) = 0; virtual bool GetWindowButtonVisibility() const = 0; virtual void SetTrafficLightPosition(base::Optional position) = 0; diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index f2d89402d987b..c8c4fe392cc4e 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -79,6 +79,7 @@ class NativeWindowMac : public NativeWindow, void SetAlwaysOnTop(ui::ZOrderLevel z_order, const std::string& level, int relative_level) override; + std::string GetAlwaysOnTopLevel() override; ui::ZOrderLevel GetZOrderLevel() override; void Center() override; void Invalidate() override; diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index d36a5500f5c6e..28a31e4d6c5de 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -875,6 +875,31 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { SetWindowLevel(level + relative_level); } +std::string NativeWindowMac::GetAlwaysOnTopLevel() { + std::string level_name = "normal"; + + int level = [window_ level]; + if (level == NSFloatingWindowLevel) { + level_name = "floating"; + } else if (level == NSTornOffMenuWindowLevel) { + level_name = "torn-off-menu"; + } else if (level == NSModalPanelWindowLevel) { + level_name = "modal-panel"; + } else if (level == NSMainMenuWindowLevel) { + level_name = "main-menu"; + } else if (level == NSStatusWindowLevel) { + level_name = "status"; + } else if (level == NSPopUpMenuWindowLevel) { + level_name = "pop-up-menu"; + } else if (level == NSScreenSaverWindowLevel) { + level_name = "screen-saver"; + } else if (level == NSDockWindowLevel) { + level_name = "dock"; + } + + return level_name; +} + void NativeWindowMac::SetWindowLevel(int unbounded_level) { int level = std::min( std::max(unbounded_level, CGWindowLevelForKey(kCGMinimumWindowLevelKey)), @@ -1803,10 +1828,15 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { // Set new parent window. // Note that this method will force the window to become visible. - if (parent && attach) + if (parent && attach) { + // Attaching a window as a child window resets its window level, so + // save and restore it afterwards. + NSInteger level = window_.level; [parent->GetNativeWindow().GetNativeNSWindow() addChildWindow:window_ ordered:NSWindowAbove]; + [window_ setLevel:level]; + } } void NativeWindowMac::SetForwardMouseMessages(bool forward) { diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index 284a15e16158a..bda6c8c1c3886 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -1418,15 +1418,12 @@ describe('BrowserWindow module', () => { describe('BrowserWindow.setAlwaysOnTop(flag, level)', () => { let w = null as unknown as BrowserWindow; + afterEach(closeAllWindows); + beforeEach(() => { w = new BrowserWindow({ show: true }); }); - afterEach(async () => { - await closeWindow(w); - w = null as unknown as BrowserWindow; - }); - it('sets the window as always on top', () => { expect(w.isAlwaysOnTop()).to.be.false('is alwaysOnTop'); w.setAlwaysOnTop(true, 'screen-saver'); @@ -1454,6 +1451,16 @@ describe('BrowserWindow module', () => { const [, alwaysOnTop] = await alwaysOnTopChanged; expect(alwaysOnTop).to.be.true('is not alwaysOnTop'); }); + + ifit(process.platform === 'darwin')('honors the alwaysOnTop level of a child window', () => { + w = new BrowserWindow({ show: false }); + const c = new BrowserWindow({ parent: w }); + c.setAlwaysOnTop(true, 'screen-saver'); + + expect(w.isAlwaysOnTop()).to.be.false(); + expect(c.isAlwaysOnTop()).to.be.true('child is not always on top'); + expect((c as any)._getAlwaysOnTopLevel()).to.equal('screen-saver'); + }); }); describe('preconnect feature', () => {