feat: nativeTheme.themeSource and a few nativeTheme fixes (#20486)

* feat: add nativeTheme.themeSource to allow apps to override Chromiums theme choice (#19960)

* feat: add nativeTheme.shouldUseDarkColorsOverride to allow apps to override Chromiums theme choice

* spec: add tests for shouldUseDarkColorsOverride

* chore: add missing forward declarations

* refactor: rename overrideShouldUseDarkColors to themeSource

* chore: only run appLevelAppearance specs on Mojave and up

* chore: update patch with more info and no define

* Update spec-main/api-native-theme-spec.ts

Co-Authored-By: Jeremy Apthorp <jeremya@chromium.org>

* Update api-native-theme-spec.ts

* Update api-native-theme-spec.ts

* Update api-native-theme-spec.ts

* fix: don't expose nativeTheme in the renderer process (#20139)

Exposing these in the renderer didn't make sense as they weren't backed
by the same instance / value store.  This API should be browser only
especially now that we have nativeTheme.themeSource.  Exposing in
//common was a mistake from the beginning.

* fix: emit updated on NativeTheme on the UI thread to avoid DCHECK (#20137)

* fix: emit updated on NativeTheme on the UI thread to avoid DCHECK

* Update atom_api_native_theme.cc

* spec: wait a few ticks for async events to emit so that test events do not leak into each other

* chore: add SetGTKDarkThemeEnabled(enabled) internal helper to allow dynamic theme selection on linux (#19964)

This is just a after-creation setter for the `darkTheme` constructor option.  This is delibrately
a method and not a property as there is no getter.

* spec: remove leftover .only
This commit is contained in:
Samuel Attard
2019-10-08 15:18:00 -07:00
committed by John Kleinschmidt
parent e17cd837ef
commit 0a9b201c34
21 changed files with 354 additions and 23 deletions

View File

@@ -2,8 +2,13 @@
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/api/atom_api_native_theme.h"
#include "shell/browser/api/atom_api_native_theme.h"
#include <string>
#include "base/task/post_task.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "shell/common/node_includes.h"
@@ -24,10 +29,31 @@ NativeTheme::~NativeTheme() {
theme_->RemoveObserver(this);
}
void NativeTheme::OnNativeThemeUpdated(ui::NativeTheme* theme) {
void NativeTheme::OnNativeThemeUpdatedOnUI() {
Emit("updated");
}
void NativeTheme::OnNativeThemeUpdated(ui::NativeTheme* theme) {
base::PostTaskWithTraits(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&NativeTheme::OnNativeThemeUpdatedOnUI,
base::Unretained(this)));
}
void NativeTheme::SetThemeSource(ui::NativeTheme::ThemeSource override) {
theme_->set_theme_source(override);
#if defined(OS_MACOSX)
// Update the macOS appearance setting for this new override value
UpdateMacOSAppearanceForOverrideValue(override);
#endif
// TODO(MarshallOfSound): Update all existing browsers windows to use GTK dark
// theme
}
ui::NativeTheme::ThemeSource NativeTheme::GetThemeSource() const {
return theme_->theme_source();
}
bool NativeTheme::ShouldUseDarkColors() {
return theme_->ShouldUseDarkColors();
}
@@ -68,6 +94,8 @@ void NativeTheme::BuildPrototype(v8::Isolate* isolate,
prototype->SetClassName(mate::StringToV8(isolate, "NativeTheme"));
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetProperty("shouldUseDarkColors", &NativeTheme::ShouldUseDarkColors)
.SetProperty("themeSource", &NativeTheme::GetThemeSource,
&NativeTheme::SetThemeSource)
.SetProperty("shouldUseHighContrastColors",
&NativeTheme::ShouldUseHighContrastColors)
.SetProperty("shouldUseInvertedColorScheme",
@@ -94,4 +122,42 @@ void Initialize(v8::Local<v8::Object> exports,
} // namespace
namespace mate {
v8::Local<v8::Value> Converter<ui::NativeTheme::ThemeSource>::ToV8(
v8::Isolate* isolate,
const ui::NativeTheme::ThemeSource& val) {
switch (val) {
case ui::NativeTheme::ThemeSource::kForcedDark:
return mate::ConvertToV8(isolate, "dark");
case ui::NativeTheme::ThemeSource::kForcedLight:
return mate::ConvertToV8(isolate, "light");
case ui::NativeTheme::ThemeSource::kSystem:
default:
return mate::ConvertToV8(isolate, "system");
}
}
bool Converter<ui::NativeTheme::ThemeSource>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
ui::NativeTheme::ThemeSource* out) {
std::string theme_source;
if (mate::ConvertFromV8(isolate, val, &theme_source)) {
if (theme_source == "dark") {
*out = ui::NativeTheme::ThemeSource::kForcedDark;
} else if (theme_source == "light") {
*out = ui::NativeTheme::ThemeSource::kForcedLight;
} else if (theme_source == "system") {
*out = ui::NativeTheme::ThemeSource::kSystem;
} else {
return false;
}
return true;
}
return false;
}
} // namespace mate
NODE_LINKED_MODULE_CONTEXT_AWARE(atom_common_native_theme, Initialize)

View File

@@ -2,11 +2,12 @@
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_COMMON_API_ATOM_API_NATIVE_THEME_H_
#define SHELL_COMMON_API_ATOM_API_NATIVE_THEME_H_
#ifndef SHELL_BROWSER_API_ATOM_API_NATIVE_THEME_H_
#define SHELL_BROWSER_API_ATOM_API_NATIVE_THEME_H_
#include "native_mate/handle.h"
#include "shell/browser/api/event_emitter.h"
#include "ui/native_theme/native_theme.h"
#include "ui/native_theme/native_theme_observer.h"
namespace electron {
@@ -25,12 +26,19 @@ class NativeTheme : public mate::EventEmitter<NativeTheme>,
NativeTheme(v8::Isolate* isolate, ui::NativeTheme* theme);
~NativeTheme() override;
void SetThemeSource(ui::NativeTheme::ThemeSource override);
#if defined(OS_MACOSX)
void UpdateMacOSAppearanceForOverrideValue(
ui::NativeTheme::ThemeSource override);
#endif
ui::NativeTheme::ThemeSource GetThemeSource() const;
bool ShouldUseDarkColors();
bool ShouldUseHighContrastColors();
bool ShouldUseInvertedColorScheme();
// ui::NativeThemeObserver:
void OnNativeThemeUpdated(ui::NativeTheme* theme) override;
void OnNativeThemeUpdatedOnUI();
private:
ui::NativeTheme* theme_;
@@ -42,4 +50,17 @@ class NativeTheme : public mate::EventEmitter<NativeTheme>,
} // namespace electron
#endif // SHELL_COMMON_API_ATOM_API_NATIVE_THEME_H_
namespace mate {
template <>
struct Converter<ui::NativeTheme::ThemeSource> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const ui::NativeTheme::ThemeSource& val);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
ui::NativeTheme::ThemeSource* out);
};
} // namespace mate
#endif // SHELL_BROWSER_API_ATOM_API_NATIVE_THEME_H_

View File

@@ -0,0 +1,37 @@
// Copyright (c) 2019 Slack Technologies, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/api/atom_api_native_theme.h"
#include "base/mac/sdk_forward_declarations.h"
#include "shell/browser/mac/atom_application.h"
namespace electron {
namespace api {
void NativeTheme::UpdateMacOSAppearanceForOverrideValue(
ui::NativeTheme::ThemeSource override) {
if (@available(macOS 10.14, *)) {
NSAppearance* new_appearance;
switch (override) {
case ui::NativeTheme::ThemeSource::kForcedDark:
new_appearance =
[NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
break;
case ui::NativeTheme::ThemeSource::kForcedLight:
new_appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
break;
case ui::NativeTheme::ThemeSource::kSystem:
default:
new_appearance = nil;
break;
}
[[NSApplication sharedApplication] setAppearance:new_appearance];
}
}
} // namespace api
} // namespace electron

View File

@@ -866,6 +866,10 @@ void TopLevelWindow::CloseFilePreview() {
window_->CloseFilePreview();
}
void TopLevelWindow::SetGTKDarkThemeEnabled(bool use_dark_theme) {
window_->SetGTKDarkThemeEnabled(use_dark_theme);
}
v8::Local<v8::Value> TopLevelWindow::GetContentView() const {
if (content_view_.IsEmpty())
return v8::Null(isolate());

View File

@@ -198,6 +198,7 @@ class TopLevelWindow : public mate::TrackableObject<TopLevelWindow>,
void SetAspectRatio(double aspect_ratio, mate::Arguments* args);
void PreviewFile(const std::string& path, mate::Arguments* args);
void CloseFilePreview();
void SetGTKDarkThemeEnabled(bool use_dark_theme);
// Public getters of NativeWindow.
v8::Local<v8::Value> GetContentView() const;

View File

@@ -218,6 +218,8 @@ class NativeWindow : public base::SupportsUserData,
const std::string& display_name);
virtual void CloseFilePreview();
virtual void SetGTKDarkThemeEnabled(bool use_dark_theme) = 0;
// Converts between content bounds and window bounds.
virtual gfx::Rect ContentBoundsToWindowBounds(
const gfx::Rect& bounds) const = 0;

View File

@@ -135,6 +135,7 @@ class NativeWindowMac : public NativeWindow {
const std::vector<mate::PersistentDictionary>& items) override;
void RefreshTouchBarItem(const std::string& item_id) override;
void SetEscapeTouchBarItem(const mate::PersistentDictionary& item) override;
void SetGTKDarkThemeEnabled(bool use_dark_theme) override {}
gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) const override;
gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) const override;

View File

@@ -213,12 +213,7 @@ NativeWindowViews::NativeWindowViews(const mate::Dictionary& options,
// Set _GTK_THEME_VARIANT to dark if we have "dark-theme" option set.
bool use_dark_theme = false;
if (options.Get(options::kDarkTheme, &use_dark_theme) && use_dark_theme) {
XDisplay* xdisplay = gfx::GetXDisplay();
XChangeProperty(xdisplay, GetAcceleratedWidget(),
XInternAtom(xdisplay, "_GTK_THEME_VARIANT", x11::False),
XInternAtom(xdisplay, "UTF8_STRING", x11::False), 8,
PropModeReplace,
reinterpret_cast<const unsigned char*>("dark"), 4);
SetGTKDarkThemeEnabled(use_dark_theme);
}
// Before the window is mapped the SetWMSpecState can not work, so we have
@@ -329,6 +324,25 @@ NativeWindowViews::~NativeWindowViews() {
#endif
}
void NativeWindowViews::SetGTKDarkThemeEnabled(bool use_dark_theme) {
#if defined(USE_X11)
XDisplay* xdisplay = gfx::GetXDisplay();
if (use_dark_theme) {
XChangeProperty(xdisplay, GetAcceleratedWidget(),
XInternAtom(xdisplay, "_GTK_THEME_VARIANT", x11::False),
XInternAtom(xdisplay, "UTF8_STRING", x11::False), 8,
PropModeReplace,
reinterpret_cast<const unsigned char*>("dark"), 4);
} else {
XChangeProperty(xdisplay, GetAcceleratedWidget(),
XInternAtom(xdisplay, "_GTK_THEME_VARIANT", x11::False),
XInternAtom(xdisplay, "UTF8_STRING", x11::False), 8,
PropModeReplace,
reinterpret_cast<const unsigned char*>("light"), 5);
}
#endif
}
void NativeWindowViews::SetContentView(views::View* view) {
if (content_view()) {
root_view_->RemoveChildView(content_view());

View File

@@ -124,6 +124,8 @@ class NativeWindowViews : public NativeWindow,
bool IsVisibleOnAllWorkspaces() override;
void SetGTKDarkThemeEnabled(bool use_dark_theme) override;
gfx::AcceleratedWidget GetAcceleratedWidget() const override;
NativeWindowHandle GetNativeWindowHandle() const override;