feat: Add getAccentColor on Linux (#48027)

* feat: Implement `getAccentColor` on Linux

* doc: Update OS support for accent color APIs
This commit is contained in:
Tau Gärtli
2025-10-21 20:26:30 +02:00
committed by GitHub
parent 4d329d466b
commit 7ec0ebc50a
4 changed files with 65 additions and 7 deletions

View File

@@ -14,7 +14,7 @@ console.log(systemPreferences.getEffectiveAppearance())
The `systemPreferences` object emits the following events:
### Event: 'accent-color-changed' _Windows_
### Event: 'accent-color-changed' _Windows_ _Linux_
Returns:
@@ -182,7 +182,7 @@ Some popular `key` and `type`s are:
Removes the `key` in `NSUserDefaults`. This can be used to restore the default
or global value of a `key` previously set with `setUserDefault`.
### `systemPreferences.getAccentColor()` _Windows_ _macOS_
### `systemPreferences.getAccentColor()`
Returns `string` - The users current system wide accent color preference in RGBA
hexadecimal form.

View File

@@ -10,6 +10,12 @@
#include "shell/common/gin_helper/handle.h"
#include "shell/common/node_includes.h"
#include "ui/gfx/animation/animation.h"
#if BUILDFLAG(IS_LINUX)
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "shell/browser/api/electron_api_system_preferences.h"
#include "shell/common/color_util.h"
#endif
namespace electron::api {
@@ -20,6 +26,11 @@ gin::DeprecatedWrapperInfo SystemPreferences::kWrapperInfo = {
SystemPreferences::SystemPreferences() {
InitializeWindow();
}
#elif BUILDFLAG(IS_LINUX)
SystemPreferences::SystemPreferences()
: ui_theme_(ui::NativeTheme::GetInstanceForNativeUi()) {
ui_theme_->AddObserver(this);
}
#else
SystemPreferences::SystemPreferences() = default;
#endif
@@ -44,6 +55,29 @@ v8::Local<v8::Value> SystemPreferences::GetAnimationSettings(
return dict.GetHandle();
}
#if BUILDFLAG(IS_LINUX)
std::string SystemPreferences::GetAccentColor() {
auto const color = ui_theme_->user_color();
if (!color.has_value())
return "";
return ToRGBAHex(*color);
}
void SystemPreferences::OnNativeThemeUpdatedOnUI() {
auto const new_accent_color = GetAccentColor();
if (current_accent_color_ == new_accent_color)
return;
Emit("accent-color-changed", new_accent_color);
current_accent_color_ = new_accent_color;
}
void SystemPreferences::OnNativeThemeUpdated(ui::NativeTheme* theme) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&SystemPreferences::OnNativeThemeUpdatedOnUI,
base::Unretained(this)));
}
#endif
// static
gin_helper::Handle<SystemPreferences> SystemPreferences::Create(
v8::Isolate* isolate) {
@@ -54,9 +88,9 @@ gin::ObjectTemplateBuilder SystemPreferences::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return gin_helper::EventEmitterMixin<
SystemPreferences>::GetObjectTemplateBuilder(isolate)
.SetMethod("getAccentColor", &SystemPreferences::GetAccentColor)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
.SetMethod("getColor", &SystemPreferences::GetColor)
.SetMethod("getAccentColor", &SystemPreferences::GetAccentColor)
.SetMethod("getMediaAccessStatus",
&SystemPreferences::GetMediaAccessStatus)
#endif

View File

@@ -18,6 +18,11 @@
#include "shell/browser/browser.h"
#include "shell/browser/browser_observer.h"
#endif
#if BUILDFLAG(IS_LINUX)
#include "base/memory/raw_ptr.h"
#include "ui/native_theme/native_theme.h"
#include "ui/native_theme/native_theme_observer.h"
#endif
namespace gin_helper {
template <typename T>
@@ -44,6 +49,9 @@ class SystemPreferences final
#if BUILDFLAG(IS_WIN)
,
public BrowserObserver
#elif BUILDFLAG(IS_LINUX)
,
public ui::NativeThemeObserver
#endif
{
public:
@@ -55,8 +63,8 @@ class SystemPreferences final
v8::Isolate* isolate) override;
const char* GetTypeName() override;
std::string GetAccentColor();
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
static std::string GetAccentColor();
std::string GetColor(gin_helper::ErrorThrower thrower,
const std::string& color);
std::string GetMediaAccessStatus(gin_helper::ErrorThrower thrower,
@@ -117,6 +125,10 @@ class SystemPreferences final
// TODO(MarshallOfSound): Write tests for these methods once we
// are running tests on a Mojave machine
v8::Local<v8::Value> GetEffectiveAppearance(v8::Isolate* isolate);
#elif BUILDFLAG(IS_LINUX)
// ui::NativeThemeObserver:
void OnNativeThemeUpdated(ui::NativeTheme* theme) override;
#endif
v8::Local<v8::Value> GetAnimationSettings(v8::Isolate* isolate);
@@ -162,6 +174,12 @@ class SystemPreferences final
// Color/high contrast mode change observer.
base::CallbackListSubscription hwnd_subscription_;
#endif
#if BUILDFLAG(IS_LINUX)
void OnNativeThemeUpdatedOnUI();
raw_ptr<ui::NativeTheme> ui_theme_;
std::string current_accent_color_;
#endif
};
} // namespace electron::api

View File

@@ -2,11 +2,17 @@ import { systemPreferences } from 'electron/main';
import { expect } from 'chai';
import { ifdescribe } from './lib/spec-helpers';
import { ifdescribe, ifit } from './lib/spec-helpers';
describe('systemPreferences module', () => {
ifdescribe(process.platform === 'win32')('systemPreferences.getAccentColor', () => {
it('should return a non-empty string', () => {
ifdescribe(['win32', 'linux'].includes(process.platform))('systemPreferences.getAccentColor', () => {
ifit(process.platform === 'linux')('should return a string', () => {
// Testing this properly (i.e. non-empty string) requires
// some tricky D-Bus mock setup.
const accentColor = systemPreferences.getAccentColor();
expect(accentColor).to.be.a('string');
});
ifit(process.platform === 'win32')('should return a non-empty string', () => {
const accentColor = systemPreferences.getAccentColor();
expect(accentColor).to.be.a('string').that.is.not.empty('accent color');
});