diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index efbd31bc6502..2f97a88faaf4 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -512,8 +512,17 @@ bool Window::IsClosable() { void Window::SetAlwaysOnTop(bool top, mate::Arguments* args) { std::string level = "floating"; + int relativeLevel = 0; + std::string error; + args->GetNext(&level); - window_->SetAlwaysOnTop(top, level); + args->GetNext(&relativeLevel); + + window_->SetAlwaysOnTop(top, level, relativeLevel, &error); + + if (!error.empty()) { + args->ThrowError(error); + } } bool Window::IsAlwaysOnTop() { diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index 3422db0ad639..6f2d5254697d 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -119,7 +119,9 @@ class NativeWindow : public base::SupportsUserData, virtual void SetClosable(bool closable) = 0; virtual bool IsClosable() = 0; virtual void SetAlwaysOnTop(bool top, - const std::string& level = "floating") = 0; + const std::string& level = "floating", + int relativeLevel = 0, + std::string* error = nullptr) = 0; virtual bool IsAlwaysOnTop() = 0; virtual void Center() = 0; virtual void SetTitle(const std::string& title) = 0; diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index 759e51e3623b..186051200a3e 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -67,7 +67,8 @@ class NativeWindowMac : public NativeWindow, bool IsFullScreenable() override; void SetClosable(bool closable) override; bool IsClosable() override; - void SetAlwaysOnTop(bool top, const std::string& level) override; + void SetAlwaysOnTop(bool top, const std::string& level, + int relativeLevel, std::string* error) override; bool IsAlwaysOnTop() override; void Center() override; void SetTitle(const std::string& title) override; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 037d0be487db..430fe48ee96f 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -1056,8 +1056,12 @@ bool NativeWindowMac::IsClosable() { return [window_ styleMask] & NSClosableWindowMask; } -void NativeWindowMac::SetAlwaysOnTop(bool top, const std::string& level) { +void NativeWindowMac::SetAlwaysOnTop(bool top, const std::string& level, + int relativeLevel, std::string* error) { int windowLevel = NSNormalWindowLevel; + CGWindowLevel maxWindowLevel = CGWindowLevelForKey(kCGMaximumWindowLevelKey); + CGWindowLevel minWindowLevel = CGWindowLevelForKey(kCGMinimumWindowLevelKey); + if (top) { if (level == "floating") { windowLevel = NSFloatingWindowLevel; @@ -1078,7 +1082,15 @@ void NativeWindowMac::SetAlwaysOnTop(bool top, const std::string& level) { windowLevel = NSDockWindowLevel; } } - [window_ setLevel:windowLevel]; + + NSInteger newLevel = windowLevel + relativeLevel; + if (newLevel >= minWindowLevel && newLevel <= maxWindowLevel) { + [window_ setLevel:newLevel]; + } else { + *error = std::string([[NSString stringWithFormat: + @"relativeLevel must be between %d and %d", minWindowLevel, + maxWindowLevel] UTF8String]); + } } bool NativeWindowMac::IsAlwaysOnTop() { diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index d5dbbaa4f7a4..16a10b3532ca 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -682,7 +682,8 @@ bool NativeWindowViews::IsClosable() { #endif } -void NativeWindowViews::SetAlwaysOnTop(bool top, const std::string& level) { +void NativeWindowViews::SetAlwaysOnTop(bool top, const std::string& level, + int relativeLevel, std::string* error) { window_->SetAlwaysOnTop(top); } diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index 110a4e9d4373..7d983b89a573 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -86,7 +86,8 @@ class NativeWindowViews : public NativeWindow, bool IsFullScreenable() override; void SetClosable(bool closable) override; bool IsClosable() override; - void SetAlwaysOnTop(bool top, const std::string& level) override; + void SetAlwaysOnTop(bool top, const std::string& level, + int relativeLevel, std::string* error) override; bool IsAlwaysOnTop() override; void Center() override; void SetTitle(const std::string& title) override; diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 306a365a5877..1dbb92508a05 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -846,13 +846,16 @@ Returns `Boolean` - Whether the window can be manually closed by user. On Linux always returns `true`. -#### `win.setAlwaysOnTop(flag[, level])` +#### `win.setAlwaysOnTop(flag[, level][, relativeLevel])` * `flag` Boolean * `level` String (optional) _macOS_ - Values include `normal`, `floating`, `torn-off-menu`, `modal-panel`, `main-menu`, `status`, `pop-up-menu`, `screen-saver`, and ~~`dock`~~ (Deprecated). The default is `floating`. See the [macOS docs][window-levels] for more details. +* `relativeLevel` Integer (optional) _macOS_ - The number of layers higher to set + this window relative to the given `level`. The default is `0`. Note that Apple + discourages setting levels higher than 1 above `screen-saver`. Sets whether the window should show always on top of other windows. After setting this, the window is still a normal window, not a toolbox window which diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index 51aac5d52daa..213d917ae936 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -524,6 +524,18 @@ describe('BrowserWindow module', function () { w.setAlwaysOnTop(true) assert.equal(w.isAlwaysOnTop(), true) }) + + it('raises an error when relativeLevel is out of bounds', function () { + if (process.platform !== 'darwin') return + + assert.throws(function () { + w.setAlwaysOnTop(true, '', -2147483644) + }) + + assert.throws(function () { + w.setAlwaysOnTop(true, '', 2147483632) + }) + }) }) describe('BrowserWindow.setAutoHideCursor(autoHide)', () => {