feat: support customizing window accent color on Windows (#47537)
* fix: support window accent color in frameless windows Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * refactor: allow customization Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * Update docs/api/structures/base-window-options.md Co-authored-by: Will Anderson <andersonw@dropbox.com> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
This commit is contained in:
parent
be53277b54
commit
75cda9e16b
5 changed files with 107 additions and 0 deletions
|
@ -95,6 +95,7 @@
|
||||||
* `color` String (optional) _Windows_ _Linux_ - The CSS color of the Window Controls Overlay when enabled. Default is the system color.
|
* `color` String (optional) _Windows_ _Linux_ - The CSS color of the Window Controls Overlay when enabled. Default is the system color.
|
||||||
* `symbolColor` String (optional) _Windows_ _Linux_ - The CSS color of the symbols on the Window Controls Overlay when enabled. Default is the system color.
|
* `symbolColor` String (optional) _Windows_ _Linux_ - The CSS color of the symbols on the Window Controls Overlay when enabled. Default is the system color.
|
||||||
* `height` Integer (optional) - The height of the title bar and Window Controls Overlay in pixels. Default is system height.
|
* `height` Integer (optional) - The height of the title bar and Window Controls Overlay in pixels. Default is system height.
|
||||||
|
* `accentColor` boolean | string (optional) _Windows_ - The accent color for the window. By default, follows user preference in System Settings. Set to `false` to explicitly disable, or set the color in Hex, RGB, RGBA, HSL, HSLA or named CSS color format. Alpha values will be ignored.
|
||||||
* `trafficLightPosition` [Point](point.md) (optional) _macOS_ -
|
* `trafficLightPosition` [Point](point.md) (optional) _macOS_ -
|
||||||
Set a custom position for the traffic light buttons in frameless windows.
|
Set a custom position for the traffic light buttons in frameless windows.
|
||||||
* `roundedCorners` boolean (optional) _macOS_ _Windows_ - Whether frameless window
|
* `roundedCorners` boolean (optional) _macOS_ _Windows_ - Whether frameless window
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
#include "base/win/windows_version.h"
|
#include "base/win/windows_version.h"
|
||||||
#include "shell/browser/ui/views/win_frame_view.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_native_widget_aura.h"
|
||||||
|
#include "shell/common/color_util.h"
|
||||||
#include "skia/ext/skia_utils_win.h"
|
#include "skia/ext/skia_utils_win.h"
|
||||||
#include "ui/display/win/screen_win.h"
|
#include "ui/display/win/screen_win.h"
|
||||||
#include "ui/gfx/color_utils.h"
|
#include "ui/gfx/color_utils.h"
|
||||||
|
@ -214,6 +215,14 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
|
||||||
|
|
||||||
overlay_button_color_ = color_utils::GetSysSkColor(COLOR_BTNFACE);
|
overlay_button_color_ = color_utils::GetSysSkColor(COLOR_BTNFACE);
|
||||||
overlay_symbol_color_ = color_utils::GetSysSkColor(COLOR_BTNTEXT);
|
overlay_symbol_color_ = color_utils::GetSysSkColor(COLOR_BTNTEXT);
|
||||||
|
|
||||||
|
bool accent_color = true;
|
||||||
|
std::string accent_color_string;
|
||||||
|
if (options.Get(options::kAccentColor, &accent_color_string)) {
|
||||||
|
accent_color_ = ParseCSSColor(accent_color_string);
|
||||||
|
} else if (options.Get(options::kAccentColor, &accent_color)) {
|
||||||
|
accent_color_ = accent_color;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
v8::Local<v8::Value> titlebar_overlay;
|
v8::Local<v8::Value> titlebar_overlay;
|
||||||
|
@ -430,6 +439,8 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
|
||||||
last_window_state_ = ui::mojom::WindowShowState::kFullscreen;
|
last_window_state_ = ui::mojom::WindowShowState::kFullscreen;
|
||||||
else
|
else
|
||||||
last_window_state_ = ui::mojom::WindowShowState::kNormal;
|
last_window_state_ = ui::mojom::WindowShowState::kNormal;
|
||||||
|
|
||||||
|
UpdateWindowAccentColor();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Listen to mouse events.
|
// Listen to mouse events.
|
||||||
|
|
|
@ -209,6 +209,7 @@ class NativeWindowViews : public NativeWindow,
|
||||||
void ResetWindowControls();
|
void ResetWindowControls();
|
||||||
void SetRoundedCorners(bool rounded);
|
void SetRoundedCorners(bool rounded);
|
||||||
void SetForwardMouseMessages(bool forward);
|
void SetForwardMouseMessages(bool forward);
|
||||||
|
void UpdateWindowAccentColor();
|
||||||
static LRESULT CALLBACK SubclassProc(HWND hwnd,
|
static LRESULT CALLBACK SubclassProc(HWND hwnd,
|
||||||
UINT msg,
|
UINT msg,
|
||||||
WPARAM w_param,
|
WPARAM w_param,
|
||||||
|
@ -305,6 +306,8 @@ class NativeWindowViews : public NativeWindow,
|
||||||
// Whether the window is currently being moved.
|
// Whether the window is currently being moved.
|
||||||
bool is_moving_ = false;
|
bool is_moving_ = false;
|
||||||
|
|
||||||
|
std::variant<bool, SkColor> accent_color_ = true;
|
||||||
|
|
||||||
std::optional<gfx::Rect> pending_bounds_change_;
|
std::optional<gfx::Rect> pending_bounds_change_;
|
||||||
|
|
||||||
// The message ID of the "TaskbarCreated" message, sent to us when we need to
|
// The message ID of the "TaskbarCreated" message, sent to us when we need to
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <wrl/client.h>
|
#include <wrl/client.h>
|
||||||
|
|
||||||
#include "base/win/atl.h" // Must be before UIAutomationCore.h
|
#include "base/win/atl.h" // Must be before UIAutomationCore.h
|
||||||
|
#include "base/win/registry.h"
|
||||||
#include "base/win/scoped_handle.h"
|
#include "base/win/scoped_handle.h"
|
||||||
#include "base/win/windows_version.h"
|
#include "base/win/windows_version.h"
|
||||||
#include "content/public/browser/browser_accessibility_state.h"
|
#include "content/public/browser/browser_accessibility_state.h"
|
||||||
|
@ -28,6 +29,53 @@ namespace electron {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
void SetWindowBorderAndCaptionColor(HWND hwnd, COLORREF color) {
|
||||||
|
if (base::win::GetVersion() < base::win::Version::WIN11)
|
||||||
|
return;
|
||||||
|
|
||||||
|
HRESULT result =
|
||||||
|
DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &color, sizeof(color));
|
||||||
|
|
||||||
|
if (FAILED(result))
|
||||||
|
LOG(WARNING) << "Failed to set caption color";
|
||||||
|
|
||||||
|
result =
|
||||||
|
DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &color, sizeof(color));
|
||||||
|
|
||||||
|
if (FAILED(result))
|
||||||
|
LOG(WARNING) << "Failed to set border color";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<DWORD> GetAccentColor() {
|
||||||
|
base::win::RegKey key;
|
||||||
|
if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\DWM",
|
||||||
|
KEY_READ) != ERROR_SUCCESS) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD accent_color = 0;
|
||||||
|
if (key.ReadValueDW(L"AccentColor", &accent_color) != ERROR_SUCCESS) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return accent_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAccentColorOnTitleBarsEnabled() {
|
||||||
|
base::win::RegKey key;
|
||||||
|
if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\DWM",
|
||||||
|
KEY_READ) != ERROR_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD enabled = 0;
|
||||||
|
if (key.ReadValueDW(L"ColorPrevalence", &enabled) != ERROR_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return enabled != 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Convert Win32 WM_QUERYENDSESSIONS to strings.
|
// Convert Win32 WM_QUERYENDSESSIONS to strings.
|
||||||
const std::vector<std::string> EndSessionToStringVec(LPARAM end_session_id) {
|
const std::vector<std::string> EndSessionToStringVec(LPARAM end_session_id) {
|
||||||
std::vector<std::string> params;
|
std::vector<std::string> params;
|
||||||
|
@ -450,6 +498,19 @@ bool NativeWindowViews::PreHandleMSG(UINT message,
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
case WM_DWMCOLORIZATIONCOLORCHANGED: {
|
||||||
|
UpdateWindowAccentColor();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case WM_SETTINGCHANGE: {
|
||||||
|
if (l_param) {
|
||||||
|
const wchar_t* setting_name = reinterpret_cast<const wchar_t*>(l_param);
|
||||||
|
std::wstring setting_str(setting_name);
|
||||||
|
if (setting_str == L"ImmersiveColorSet")
|
||||||
|
UpdateWindowAccentColor();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -509,6 +570,35 @@ void NativeWindowViews::HandleSizeEvent(WPARAM w_param, LPARAM l_param) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NativeWindowViews::UpdateWindowAccentColor() {
|
||||||
|
if (base::win::GetVersion() < base::win::Version::WIN11)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!IsAccentColorOnTitleBarsEnabled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
COLORREF border_color;
|
||||||
|
if (std::holds_alternative<bool>(accent_color_)) {
|
||||||
|
// Don't set accent color if the user has disabled it.
|
||||||
|
if (!std::get<bool>(accent_color_))
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::optional<DWORD> accent_color = GetAccentColor();
|
||||||
|
if (!accent_color.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
border_color =
|
||||||
|
RGB(GetRValue(accent_color.value()), GetGValue(accent_color.value()),
|
||||||
|
GetBValue(accent_color.value()));
|
||||||
|
} else {
|
||||||
|
SkColor color = std::get<SkColor>(accent_color_);
|
||||||
|
border_color =
|
||||||
|
RGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
SetWindowBorderAndCaptionColor(GetAcceleratedWidget(), border_color);
|
||||||
|
}
|
||||||
|
|
||||||
void NativeWindowViews::ResetWindowControls() {
|
void NativeWindowViews::ResetWindowControls() {
|
||||||
// If a given window was minimized and has since been
|
// If a given window was minimized and has since been
|
||||||
// unminimized (restored/maximized), ensure the WCO buttons
|
// unminimized (restored/maximized), ensure the WCO buttons
|
||||||
|
|
|
@ -123,6 +123,8 @@ inline constexpr std::string_view kRoundedCorners = "roundedCorners";
|
||||||
|
|
||||||
inline constexpr std::string_view ktitleBarOverlay = "titleBarOverlay";
|
inline constexpr std::string_view ktitleBarOverlay = "titleBarOverlay";
|
||||||
|
|
||||||
|
inline constexpr std::string_view kAccentColor = "accentColor";
|
||||||
|
|
||||||
// The color to use as the theme and symbol colors respectively for Window
|
// The color to use as the theme and symbol colors respectively for Window
|
||||||
// Controls Overlay if enabled on Windows.
|
// Controls Overlay if enabled on Windows.
|
||||||
inline constexpr std::string_view kOverlayButtonColor = "color";
|
inline constexpr std::string_view kOverlayButtonColor = "color";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue