diff --git a/docs/api/system-preferences.md b/docs/api/system-preferences.md index 8af85f757d..ce5f4bf8b5 100644 --- a/docs/api/system-preferences.md +++ b/docs/api/system-preferences.md @@ -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. diff --git a/shell/browser/api/electron_api_system_preferences.cc b/shell/browser/api/electron_api_system_preferences.cc index 053cd89728..52ac57acd0 100644 --- a/shell/browser/api/electron_api_system_preferences.cc +++ b/shell/browser/api/electron_api_system_preferences.cc @@ -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 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::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 diff --git a/shell/browser/api/electron_api_system_preferences.h b/shell/browser/api/electron_api_system_preferences.h index 5cf6632a86..442f2e2c11 100644 --- a/shell/browser/api/electron_api_system_preferences.h +++ b/shell/browser/api/electron_api_system_preferences.h @@ -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 @@ -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 GetEffectiveAppearance(v8::Isolate* isolate); + +#elif BUILDFLAG(IS_LINUX) + // ui::NativeThemeObserver: + void OnNativeThemeUpdated(ui::NativeTheme* theme) override; #endif v8::Local 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_theme_; + std::string current_accent_color_; +#endif }; } // namespace electron::api diff --git a/spec/api-system-preferences-spec.ts b/spec/api-system-preferences-spec.ts index 05de0b91e8..5c10b43318 100644 --- a/spec/api-system-preferences-spec.ts +++ b/spec/api-system-preferences-spec.ts @@ -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'); });