Browse Source

feat: support Mica/Acrylic on Windows (#38357)

Shelley Vohr 1 year ago
parent
commit
662fa261da

+ 18 - 0
docs/api/browser-window.md

@@ -262,6 +262,9 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
     `tooltip`, `content`, `under-window`, or `under-page`. Please note that
     `appearance-based`, `light`, `dark`, `medium-light`, and `ultra-dark` are
     deprecated and have been removed in macOS Catalina (10.15).
+  * `backgroundMaterial` string (optional) _Windows_ - Set the window's
+    system-drawn background material, including behind the non-client area.
+    Can be `auto`, `none`, `mica`, `acrylic` or `tabbed`. See [win.setBackgroundMaterial](#winsetbackgroundmaterialmaterial-windows) for more information.
   * `zoomToPageWidth` boolean (optional) _macOS_ - Controls the behavior on
     macOS when option-clicking the green stoplight button on the toolbar or by
     clicking the Window > Zoom menu item. If `true`, the window will grow to
@@ -1844,6 +1847,21 @@ will remove the vibrancy effect on the window.
 Note that `appearance-based`, `light`, `dark`, `medium-light`, and `ultra-dark` have been
 deprecated and will be removed in an upcoming version of macOS.
 
+#### `win.setBackgroundMaterial(material)` _Windows_
+
+* `material` string
+  * `auto` - Let the Desktop Window Manager (DWM) automatically decide the system-drawn backdrop material for this window. This is the default.
+  * `none` - Don't draw any system backdrop.
+  * `mica` - Draw the backdrop material effect corresponding to a long-lived window.
+  * `acrylic` - Draw the backdrop material effect corresponding to a transient window.
+  * `tabbed` - Draw the backdrop material effect corresponding to a window with a tabbed title bar.
+
+This method sets the browser window's system-drawn background material, including behind the non-client area.
+
+See the [Windows documentation](https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type) for more details.
+
+**Note:** This method is only supported on Windows 11 22H2 and up.
+
 #### `win.setWindowButtonPosition(position)` _macOS_
 
 * `position` [Point](structures/point.md) | null

+ 5 - 0
shell/browser/api/electron_api_base_window.cc

@@ -864,6 +864,10 @@ void BaseWindow::SetVibrancy(v8::Isolate* isolate, v8::Local<v8::Value> value) {
   window_->SetVibrancy(type);
 }
 
+void BaseWindow::SetBackgroundMaterial(const std::string& material_type) {
+  window_->SetBackgroundMaterial(material_type);
+}
+
 #if BUILDFLAG(IS_MAC)
 std::string BaseWindow::GetAlwaysOnTopLevel() {
   return window_->GetAlwaysOnTopLevel();
@@ -1267,6 +1271,7 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate,
       .SetMethod("setAutoHideCursor", &BaseWindow::SetAutoHideCursor)
 #endif
       .SetMethod("setVibrancy", &BaseWindow::SetVibrancy)
+      .SetMethod("setBackgroundMaterial", &BaseWindow::SetBackgroundMaterial)
 
 #if BUILDFLAG(IS_MAC)
       .SetMethod("isHiddenInMissionControl",

+ 1 - 0
shell/browser/api/electron_api_base_window.h

@@ -190,6 +190,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
   bool IsVisibleOnAllWorkspaces();
   void SetAutoHideCursor(bool auto_hide);
   virtual void SetVibrancy(v8::Isolate* isolate, v8::Local<v8::Value> value);
+  void SetBackgroundMaterial(const std::string& vibrancy);
 
 #if BUILDFLAG(IS_MAC)
   std::string GetAlwaysOnTopLevel();

+ 7 - 0
shell/browser/native_window.cc

@@ -249,6 +249,11 @@ void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) {
   if (options.Get(options::kVibrancyType, &type)) {
     SetVibrancy(type);
   }
+#elif BUILDFLAG(IS_WIN)
+  std::string material;
+  if (options.Get(options::kBackgroundMaterial, &material)) {
+    SetBackgroundMaterial(material);
+  }
 #endif
   std::string color;
   if (options.Get(options::kBackgroundColor, &color)) {
@@ -445,6 +450,8 @@ bool NativeWindow::AddTabbedWindow(NativeWindow* window) {
 
 void NativeWindow::SetVibrancy(const std::string& type) {}
 
+void NativeWindow::SetBackgroundMaterial(const std::string& type) {}
+
 void NativeWindow::SetTouchBar(
     std::vector<gin_helper::PersistentDictionary> items) {}
 

+ 2 - 0
shell/browser/native_window.h

@@ -215,6 +215,8 @@ class NativeWindow : public base::SupportsUserData,
   // Vibrancy API
   virtual void SetVibrancy(const std::string& type);
 
+  virtual void SetBackgroundMaterial(const std::string& type);
+
   // Traffic Light API
 #if BUILDFLAG(IS_MAC)
   virtual void SetWindowButtonVisibility(bool visible) = 0;

+ 30 - 0
shell/browser/native_window_views.cc

@@ -5,6 +5,7 @@
 #include "shell/browser/native_window_views.h"
 
 #if BUILDFLAG(IS_WIN)
+#include <dwmapi.h>
 #include <wrl/client.h>
 #endif
 
@@ -68,6 +69,7 @@
 
 #elif BUILDFLAG(IS_WIN)
 #include "base/win/win_util.h"
+#include "base/win/windows_version.h"
 #include "content/public/common/color_parser.h"
 #include "shell/browser/ui/views/win_frame_view.h"
 #include "shell/browser/ui/win/electron_desktop_native_widget_aura.h"
@@ -82,6 +84,19 @@ namespace electron {
 
 #if BUILDFLAG(IS_WIN)
 
+DWM_SYSTEMBACKDROP_TYPE GetBackdropFromString(const std::string& material) {
+  if (material == "none") {
+    return DWMSBT_NONE;
+  } else if (material == "acrylic") {
+    return DWMSBT_TRANSIENTWINDOW;
+  } else if (material == "mica") {
+    return DWMSBT_MAINWINDOW;
+  } else if (material == "tabbed") {
+    return DWMSBT_TABBEDWINDOW;
+  }
+  return DWMSBT_AUTO;
+}
+
 // Similar to the ones in display::win::ScreenWin, but with rounded values
 // These help to avoid problems that arise from unresizable windows where the
 // original ceil()-ed values can cause calculation errors, since converting
@@ -1392,6 +1407,21 @@ bool NativeWindowViews::IsMenuBarVisible() {
   return root_view_->IsMenuBarVisible();
 }
 
+void NativeWindowViews::SetBackgroundMaterial(const std::string& material) {
+#if BUILDFLAG(IS_WIN)
+  // DWMWA_USE_HOSTBACKDROPBRUSH is only supported on Windows 11 22H2 and up.
+  if (base::win::GetVersion() < base::win::Version::WIN11_22H2)
+    return;
+
+  DWM_SYSTEMBACKDROP_TYPE backdrop_type = GetBackdropFromString(material);
+  HRESULT result =
+      DwmSetWindowAttribute(GetAcceleratedWidget(), DWMWA_SYSTEMBACKDROP_TYPE,
+                            &backdrop_type, sizeof(backdrop_type));
+  if (FAILED(result))
+    LOG(WARNING) << "Failed to set background material to " << material;
+#endif
+}
+
 void NativeWindowViews::SetVisibleOnAllWorkspaces(
     bool visible,
     bool visibleOnFullScreen,

+ 1 - 0
shell/browser/native_window_views.h

@@ -137,6 +137,7 @@ class NativeWindowViews : public NativeWindow,
   bool IsMenuBarAutoHide() override;
   void SetMenuBarVisibility(bool visible) override;
   bool IsMenuBarVisible() override;
+  void SetBackgroundMaterial(const std::string& type) override;
 
   void SetVisibleOnAllWorkspaces(bool visible,
                                  bool visibleOnFullScreen,

+ 3 - 0
shell/common/options_switches.cc

@@ -110,6 +110,9 @@ const char kWebPreferences[] = "webPreferences";
 // Add a vibrancy effect to the browser window
 const char kVibrancyType[] = "vibrancy";
 
+// Add a vibrancy effect to the browser window.
+const char kBackgroundMaterial[] = "backgroundMaterial";
+
 // Specify how the material appearance should reflect window activity state on
 // macOS.
 const char kVisualEffectState[] = "visualEffectState";

+ 1 - 0
shell/common/options_switches.h

@@ -55,6 +55,7 @@ extern const char kOpacity[];
 extern const char kFocusable[];
 extern const char kWebPreferences[];
 extern const char kVibrancyType[];
+extern const char kBackgroundMaterial[];
 extern const char kVisualEffectState[];
 extern const char kTrafficLightPosition[];
 extern const char kRoundedCorners[];