fix: abnormal behavior of windows background material (#47386)

* fix: abnormal behavior of windows background material

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* chore: update patches

* fix: setting background material after init

---------

Co-authored-by: zoy <zoy-l@outlook.com>
Co-authored-by: patchup[bot] <73610968+patchup[bot]@users.noreply.github.com>
This commit is contained in:
Shelley Vohr
2025-07-17 14:50:12 +02:00
committed by GitHub
parent 895bf9103e
commit 31b18c9830
10 changed files with 195 additions and 107 deletions

View File

@@ -850,8 +850,8 @@ void BaseWindow::SetVibrancy(v8::Isolate* isolate,
window_->SetVibrancy(type, animation_duration_ms);
}
void BaseWindow::SetBackgroundMaterial(const std::string& material_type) {
window_->SetBackgroundMaterial(material_type);
void BaseWindow::SetBackgroundMaterial(const std::string& material) {
window_->SetBackgroundMaterial(material);
}
#if BUILDFLAG(IS_MAC)

View File

@@ -196,7 +196,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
virtual void SetVibrancy(v8::Isolate* isolate,
v8::Local<v8::Value> value,
gin_helper::Arguments* args);
void SetBackgroundMaterial(const std::string& vibrancy);
virtual void SetBackgroundMaterial(const std::string& material);
#if BUILDFLAG(IS_MAC)
std::string GetAlwaysOnTopLevel() const;

View File

@@ -4,6 +4,7 @@
#include "shell/browser/api/electron_api_browser_window.h"
#include "base/containers/fixed_flat_set.h"
#include "content/browser/renderer_host/render_widget_host_owner_delegate.h" // nogncheck
#include "content/browser/web_contents/web_contents_impl.h" // nogncheck
#include "content/public/browser/render_process_host.h"
@@ -247,6 +248,18 @@ void BrowserWindow::SetBackgroundColor(const std::string& color_name) {
}
}
void BrowserWindow::SetBackgroundMaterial(const std::string& material) {
BaseWindow::SetBackgroundMaterial(material);
static constexpr auto materialTypes =
base::MakeFixedFlatSet<std::string_view>({"tabbed", "mica", "acrylic"});
if (materialTypes.contains(material)) {
SetBackgroundColor(ToRGBAHex(SK_ColorTRANSPARENT));
} else if (material == "none") {
SetBackgroundColor(ToRGBAHex(SK_ColorWHITE));
}
}
void BrowserWindow::FocusOnWebView() {
web_contents()->GetRenderViewHost()->GetWidget()->Focus();
}

View File

@@ -64,6 +64,7 @@ class BrowserWindow : public BaseWindow,
void Focus() override;
void Blur() override;
void SetBackgroundColor(const std::string& color_name) override;
void SetBackgroundMaterial(const std::string& material) override;
void OnWindowShow() override;
void OnWindowHide() override;

View File

@@ -233,6 +233,10 @@ class NativeWindow : public base::SupportsUserData,
// Vibrancy API
virtual void SetVibrancy(const std::string& type, int duration);
const std::string& background_material() const {
return background_material_;
}
virtual void SetBackgroundMaterial(const std::string& type);
// Traffic Light API

View File

@@ -80,6 +80,7 @@
#include "base/win/windows_version.h"
#include "shell/browser/ui/views/win_frame_view.h"
#include "shell/browser/ui/win/electron_desktop_native_widget_aura.h"
#include "shell/browser/ui/win/electron_desktop_window_tree_host_win.h"
#include "shell/common/color_util.h"
#include "skia/ext/skia_utils_win.h"
#include "ui/display/win/screen_win.h"
@@ -352,6 +353,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
if (!has_frame()) {
// Set Window style so that we get a minimize and maximize animation when
// frameless.
DWORD frame_style = WS_CAPTION | WS_OVERLAPPED;
if (resizable_)
frame_style |= WS_THICKFRAME;
@@ -683,13 +685,7 @@ void NativeWindowViews::SetEnabledInternal(bool enable) {
void NativeWindowViews::Maximize() {
#if BUILDFLAG(IS_WIN)
if (IsTranslucent()) {
// Semi-transparent windows with backgroundMaterial not set to 'none', and
// not fully transparent, require manual handling of rounded corners when
// maximized.
if (rounded_corner_)
SetRoundedCorners(false);
if (transparent()) {
restore_bounds_ = GetBounds();
auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
GetNativeWindow());
@@ -713,15 +709,10 @@ void NativeWindowViews::Unmaximize() {
return;
#if BUILDFLAG(IS_WIN)
if (IsTranslucent()) {
if (transparent()) {
SetBounds(restore_bounds_, false);
NotifyWindowUnmaximize();
if (transparent()) {
UpdateThickFrame();
}
if (rounded_corner_) {
SetRoundedCorners(true);
}
UpdateThickFrame();
return;
}
#endif
@@ -738,7 +729,7 @@ bool NativeWindowViews::IsMaximized() const {
return true;
#if BUILDFLAG(IS_WIN)
if (IsTranslucent() && !IsMinimized()) {
if (transparent() && !IsMinimized()) {
// If the window is the same dimensions and placement as the
// display, we consider it maximized.
auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
@@ -760,15 +751,10 @@ void NativeWindowViews::Minimize() {
void NativeWindowViews::Restore() {
#if BUILDFLAG(IS_WIN)
if (IsMaximized() && IsTranslucent()) {
if (IsMaximized() && transparent()) {
SetBounds(restore_bounds_, false);
NotifyWindowRestore();
if (transparent()) {
UpdateThickFrame();
}
if (rounded_corner_) {
SetRoundedCorners(true);
}
UpdateThickFrame();
return;
}
#endif
@@ -914,7 +900,7 @@ gfx::Size NativeWindowViews::GetContentSize() const {
gfx::Rect NativeWindowViews::GetNormalBounds() const {
#if BUILDFLAG(IS_WIN)
if (IsMaximized() && IsTranslucent())
if (IsMaximized() && transparent())
return restore_bounds_;
#endif
return widget()->GetRestoredBounds();
@@ -1555,25 +1541,53 @@ void NativeWindowViews::SetBackgroundMaterial(const std::string& material) {
return;
DWM_SYSTEMBACKDROP_TYPE backdrop_type = GetBackdropFromString(material);
HRESULT result =
DwmSetWindowAttribute(GetAcceleratedWidget(), DWMWA_SYSTEMBACKDROP_TYPE,
&backdrop_type, sizeof(backdrop_type));
const bool is_translucent = backdrop_type != DWMSBT_NONE &&
backdrop_type != DWMSBT_AUTO && !has_frame();
HWND hwnd = GetAcceleratedWidget();
// We need to update margins ourselves since Chromium won't.
// See: ui/views/widget/widget_hwnd_utils.cc#157
// See: src/ui/views/win/hwnd_message_handler.cc#1793
MARGINS m = {0, 0, 0, 0};
if (is_translucent)
m = {-1, -1, -1, -1};
HRESULT result = DwmExtendFrameIntoClientArea(hwnd, &m);
if (FAILED(result))
LOG(WARNING) << "Failed to extend frame into client area";
result = DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE,
&backdrop_type, sizeof(backdrop_type));
if (FAILED(result))
LOG(WARNING) << "Failed to set background material to " << material;
auto* desktop_window_tree_host =
static_cast<ElectronDesktopWindowTreeHostWin*>(
GetNativeWindow()->GetHost());
// Synchronize the internal state; otherwise, the background material may not
// work properly.
if (desktop_window_tree_host) {
desktop_window_tree_host->SetIsTranslucent(is_translucent);
}
auto* desktop_native_widget_aura =
static_cast<ElectronDesktopNativeWidgetAura*>(widget()->native_widget());
desktop_native_widget_aura->UpdateWindowTransparency();
// For frameless windows with a background material set, we also need to
// remove the caption color so it doesn't render a caption bar (since the
// window is frameless)
COLORREF caption_color = DWMWA_COLOR_DEFAULT;
if (backdrop_type != DWMSBT_NONE && backdrop_type != DWMSBT_AUTO &&
!has_frame()) {
caption_color = DWMWA_COLOR_NONE;
}
result = DwmSetWindowAttribute(GetAcceleratedWidget(), DWMWA_CAPTION_COLOR,
&caption_color, sizeof(caption_color));
COLORREF caption_color =
is_translucent ? DWMWA_COLOR_NONE : DWMWA_COLOR_DEFAULT;
result = DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &caption_color,
sizeof(caption_color));
if (FAILED(result))
LOG(WARNING) << "Failed to set caption color to transparent";
// Activate the non-client area of the window
DefWindowProc(hwnd, WM_NCACTIVATE, TRUE, has_frame() ? 0 : -1);
#endif
}
@@ -1901,7 +1915,7 @@ ui::mojom::WindowShowState NativeWindowViews::GetRestoredState() {
if (IsMaximized()) {
#if BUILDFLAG(IS_WIN)
// Restore maximized state for windows that are not translucent.
if (!IsTranslucent()) {
if (!transparent()) {
return ui::mojom::WindowShowState::kMaximized;
}
#else

View File

@@ -64,7 +64,16 @@ bool ElectronDesktopWindowTreeHostWin::GetDwmFrameInsetsInPixels(
gfx::Insets* insets) const {
// Set DWMFrameInsets to prevent maximized frameless window from bleeding
// into other monitors.
if (IsMaximized() && !native_window_view_->has_frame()) {
// We avoid doing this when the window is translucent (e.g. using
// backgroundMaterial effects), because setting zero insets can interfere
// with DWM rendering of blur or acrylic, potentially causing visual
// glitches.
const std::string& bg_material = native_window_view_->background_material();
if (!bg_material.empty() && bg_material != "none") {
return false;
}
// This would be equivalent to calling:
// DwmExtendFrameIntoClientArea({0, 0, 0, 0});
//