From 78bec375f4a2adf10225369ed37d7d33153425e0 Mon Sep 17 00:00:00 2001 From: Julien Isorce Date: Wed, 17 Apr 2019 15:34:08 -0700 Subject: [PATCH] feat: Can a window always on top but behind the taskbar on Win32 For now it only adds the ability to place the window below the task bar while still being always on top. Previous behaviour was always showing the window above the task bar when top is true. We keep this default behaviour, i.e. when the 'level' parameter is omitted. https://github.com/electron/electron/issues/18933 Notes: Can set a window always on top but behind the taskbar on Windows --- docs/api/browser-window.md | 13 +++++++---- package.json | 2 +- shell/browser/native_window_views.cc | 33 ++++++++++++++++++++++++++-- shell/browser/native_window_views.h | 6 +++++ 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 930f624ab0ef6..c254e354aac78 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -1100,10 +1100,15 @@ On Linux always returns `true`. #### `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. +* `level` String (optional) _macOS_ _Windows_ - Values include `normal`, + `floating`, `torn-off-menu`, `modal-panel`, `main-menu`, `status`, + `pop-up-menu`, `screen-saver`, and ~~`dock`~~ (Deprecated). The default is + `floating` when `flag` is true. The `level` is reset to `normal` when the + flag is false. Note that from `floating` to `status` included, the window is + placed below the Dock on macOS and below the taskbar on Windows. From + `pop-up-menu` to a higher it is shown above the Dock on macOS and above the + taskbar on Windows. 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`. diff --git a/package.json b/package.json index 0269e2587822b..4f4c53ab54b2a 100644 --- a/package.json +++ b/package.json @@ -127,4 +127,4 @@ "git add filenames.auto.gni" ] } -} \ No newline at end of file +} diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 72585b84b1abf..d5a3beb5edae3 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -13,6 +13,7 @@ #include #include +#include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "content/public/browser/browser_thread.h" #include "native_mate/dictionary.h" @@ -69,6 +70,8 @@ namespace electron { namespace { #if defined(OS_WIN) +const LPCWSTR kUniqueTaskBarClassName = L"Shell_TrayWnd"; + void FlipWindowStyle(HWND handle, bool on, DWORD flag) { DWORD style = ::GetWindowLong(handle, GWL_STYLE); if (on) @@ -757,6 +760,19 @@ void NativeWindowViews::SetAlwaysOnTop(bool top, NativeWindow::NotifyWindowAlwaysOnTopChanged(); widget()->SetAlwaysOnTop(top); + +#if defined(OS_WIN) + // Reset the placement flag. + behind_task_bar_ = false; + if (top) { + // On macOS the window is placed behind the Dock for the following levels. + // Re-use the same names on Windows to make it easier for the user. + static const std::vector levels = { + "floating", "torn-off-menu", "modal-panel", "main-menu", "status"}; + behind_task_bar_ = base::Contains(levels, level); + } +#endif + MoveBehindTaskBarIfNeeded(); } bool NativeWindowViews::IsAlwaysOnTop() { @@ -1184,10 +1200,12 @@ void NativeWindowViews::OnWidgetActivationChanged(views::Widget* changed_widget, if (changed_widget != widget()) return; - if (active) + if (active) { + MoveBehindTaskBarIfNeeded(); NativeWindow::NotifyWindowFocus(); - else + } else { NativeWindow::NotifyWindowBlur(); + } // Hide menu bar when window is blured. if (!active && IsMenuBarAutoHide() && IsMenuBarVisible()) @@ -1346,6 +1364,17 @@ ui::WindowShowState NativeWindowViews::GetRestoredState() { return ui::SHOW_STATE_NORMAL; } +void NativeWindowViews::MoveBehindTaskBarIfNeeded() { +#if defined(OS_WIN) + if (behind_task_bar_) { + const HWND task_bar_hwnd = ::FindWindow(kUniqueTaskBarClassName, nullptr); + ::SetWindowPos(GetAcceleratedWidget(), task_bar_hwnd, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + } +#endif + // TODO(julien.isorce): Implement X11 case. +} + // static NativeWindow* NativeWindow::Create(const mate::Dictionary& options, NativeWindow* parent) { diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index 16a7538dd71c9..46fe042255df7 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -214,6 +214,9 @@ class NativeWindowViews : public NativeWindow, // Returns the restore state for the window. ui::WindowShowState GetRestoredState(); + // Maintain window placement. + void MoveBehindTaskBarIfNeeded(); + std::unique_ptr root_view_; // The view should be focused by default. @@ -280,6 +283,9 @@ class NativeWindowViews : public NativeWindow, bool forwarding_mouse_messages_ = false; HWND legacy_window_ = NULL; bool layered_ = false; + + // Set to true if the window is always on top and behind the task bar. + bool behind_task_bar_ = false; #endif // Handles unhandled keyboard messages coming back from the renderer process.