Compare commits

...

1 Commits

Author SHA1 Message Date
Tau Gärtli
ed83ccb8db feat: Add getAccentColor on Linux (#48027)
* feat: Implement `getAccentColor` on Linux

* doc: Update OS support for accent color APIs
2026-01-26 14:49:07 -05:00
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

@@ -11,6 +11,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 {
@@ -21,6 +27,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
@@ -45,6 +56,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) {
@@ -55,9 +89,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

@@ -17,6 +17,11 @@
#include "shell/browser/browser_observer.h"
#include "ui/gfx/sys_color_change_listener.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
,
public BrowserObserver,
public gfx::SysColorChangeListener
#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);
@@ -161,6 +173,12 @@ class SystemPreferences final
std::unique_ptr<gfx::ScopedSysColorChangeListener> color_change_listener_;
#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');
});