Skip to content

Commit efa1818

Browse files
feat: add new nativeTheme API (electron#19656)
* feat: add new nativeTheme API * chore: deprecate and clean up old systemPreferences theme APIs in favor of new nativeTheme module * chore: clean up and deprecate things per feedback * chore: add tests for deprecate and clean up invert impl * build: when is a boolean not a boolean???
1 parent 246187a commit efa1818

16 files changed

+343
-29
lines changed

docs/api/native-theme.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# nativeTheme
2+
3+
> Read and respond to changes in Chromium's native color theme.
4+
5+
Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
6+
7+
## Events
8+
9+
The `nativeTheme` module emits the following events:
10+
11+
### Event: 'updated'
12+
13+
Emitted when something in the underlying NativeTheme has changed. This normally
14+
means that either the value of `shouldUseDarkColors`,
15+
`shouldUseHighContrastColors` or `shouldUseInvertedColorScheme` has changed.
16+
You will have to check them to determine which one has changed.
17+
18+
## Properties
19+
20+
The `nativeTheme` module has the following properties:
21+
22+
### `nativeTheme.shouldUseDarkColors` _Readonly_
23+
24+
A `Boolean` for if the OS / Chromium currently has a dark mode enabled or is
25+
being instructed to show a dark-style UI.
26+
27+
### `nativeTheme.shouldUseHighContrastColors` _macOS_ _Windows_ _Readonly_
28+
29+
A `Boolean` for if the OS / Chromium currently has high-contrast mode enabled
30+
or is being instructed to show a high-constrast UI.
31+
32+
### `nativeTheme.shouldUseInvertedColorScheme` _macOS_ _Windows_ _Readonly_
33+
34+
A `Boolean` for if the OS / Chromium currently has an inverted color scheme
35+
or is being instructed to use an inverted color scheme.

docs/api/system-preferences.md

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,34 @@ Returns:
2727

2828
* `event` Event
2929

30-
### Event: 'inverted-color-scheme-changed' _Windows_
30+
### Event: 'inverted-color-scheme-changed' _Windows_ _Deprecated_
3131

3232
Returns:
3333

3434
* `event` Event
3535
* `invertedColorScheme` Boolean - `true` if an inverted color scheme (a high contrast color scheme with light text and dark backgrounds) is being used, `false` otherwise.
3636

37-
### Event: 'high-contrast-color-scheme-changed' _Windows_
37+
**Deprecated:** Should use the new [`updated`](native-theme.md#event-updated) event on the `nativeTheme` module.
38+
39+
### Event: 'high-contrast-color-scheme-changed' _Windows_ _Deprecated_
3840

3941
Returns:
4042

4143
* `event` Event
4244
* `highContrastColorScheme` Boolean - `true` if a high contrast theme is being used, `false` otherwise.
4345

46+
**Deprecated:** Should use the new [`updated`](native-theme.md#event-updated) event on the `nativeTheme` module.
47+
4448
## Methods
4549

46-
### `systemPreferences.isDarkMode()` _macOS_ _Windows_
50+
### `systemPreferences.isDarkMode()` _macOS_ _Windows_ _Deprecated_
4751

4852
Returns `Boolean` - Whether the system is in Dark Mode.
4953

5054
**Note:** On macOS 10.15 Catalina in order for this API to return the correct value when in the "automatic" dark mode setting you must either have `NSRequiresAquaSystemAppearance=false` in your `Info.plist` or be on Electron `>=7.0.0`. See the [dark mode guide](../tutorial/mojave-dark-mode-guide.md) for more information.
5155

56+
**Deprecated:** Should use the new [`nativeTheme.shouldUseDarkColors`](native-theme.md#nativethemeshouldusedarkcolors-readonly) API.
57+
5258
### `systemPreferences.isSwipeTrackingFromScrollEventsEnabled()` _macOS_
5359

5460
Returns `Boolean` - Whether the Swipe between pages setting is on.
@@ -342,14 +348,18 @@ Returns `String` - The standard system color formatted as `#RRGGBBAA`.
342348

343349
Returns one of several standard system colors that automatically adapt to vibrancy and changes in accessibility settings like 'Increase contrast' and 'Reduce transparency'. See [Apple Documentation](https://developer.apple.com/design/human-interface-guidelines/macos/visual-design/color#system-colors) for more details.
344350

345-
### `systemPreferences.isInvertedColorScheme()` _Windows_
351+
### `systemPreferences.isInvertedColorScheme()` _Windows_ _Deprecated_
346352

347353
Returns `Boolean` - `true` if an inverted color scheme (a high contrast color scheme with light text and dark backgrounds) is active, `false` otherwise.
348354

349-
### `systemPreferences.isHighContrastColorScheme()` _macOS_ _Windows_
355+
**Deprecated:** Should use the new [`nativeTheme.shouldUseInvertedColorScheme`](native-theme.md#nativethemeshoulduseinvertedcolorscheme-macos-windows-readonly) API.
356+
357+
### `systemPreferences.isHighContrastColorScheme()` _macOS_ _Windows_ _Deprecated_
350358

351359
Returns `Boolean` - `true` if a high contrast theme is active, `false` otherwise.
352360

361+
**Depreacted:** Should use the new [`nativeTheme.shouldUseHighContrastColors`](native-theme.md#nativethemeshouldusehighcontrastcolors-macos-windows-readonly) API.
362+
353363
### `systemPreferences.getEffectiveAppearance()` _macOS_
354364

355365
Returns `String` - Can be `dark`, `light` or `unknown`.
@@ -365,7 +375,9 @@ using `electron-packager` or `electron-forge` just set the `enableDarwinDarkMode
365375
packager option to `true`. See the [Electron Packager API](https://github.com/electron/electron-packager/blob/master/docs/api.md#darwindarkmodesupport)
366376
for more details.
367377

368-
### `systemPreferences.getAppLevelAppearance()` _macOS_
378+
**[Deprecated](modernization/property-updates.md)**
379+
380+
### `systemPreferences.getAppLevelAppearance()` _macOS_ _Deprecated_
369381

370382
Returns `String` | `null` - Can be `dark`, `light` or `unknown`.
371383

@@ -375,7 +387,7 @@ You can use the `setAppLevelAppearance` API to set this value.
375387

376388
**[Deprecated](modernization/property-updates.md)**
377389

378-
### `systemPreferences.setAppLevelAppearance(appearance)` _macOS_
390+
### `systemPreferences.setAppLevelAppearance(appearance)` _macOS_ _Deprecated_
379391

380392
* `appearance` String | null - Can be `dark` or `light`
381393

@@ -450,10 +462,25 @@ Returns an object with system animation settings.
450462

451463
### `systemPreferences.appLevelAppearance` _macOS_
452464

453-
A `String` property that determines the macOS appearance setting for
465+
A `String` property that can be `dark`, `light` or `unknown`. It determines the macOS appearance setting for
454466
your application. This maps to values in: [NSApplication.appearance](https://developer.apple.com/documentation/appkit/nsapplication/2967170-appearance?language=objc). Setting this will override the
455467
system default as well as the value of `getEffectiveAppearance`.
456468

457469
Possible values that can be set are `dark` and `light`, and possible return values are `dark`, `light`, and `unknown`.
458470

459471
This property is only available on macOS 10.14 Mojave or newer.
472+
473+
### `systemPreferences.effectiveAppearance` _macOS_ _Readonly_
474+
475+
A `String` property that can be `dark`, `light` or `unknown`.
476+
477+
Returns the macOS appearance setting that is currently applied to your application,
478+
maps to [NSApplication.effectiveAppearance](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc)
479+
480+
Please note that until Electron is built targeting the 10.14 SDK, your application's
481+
`effectiveAppearance` will default to 'light' and won't inherit the OS preference. In
482+
the interim in order for your application to inherit the OS preference you must set the
483+
`NSRequiresAquaSystemAppearance` key in your apps `Info.plist` to `false`. If you are
484+
using `electron-packager` or `electron-forge` just set the `enableDarwinDarkMode`
485+
packager option to `true`. See the [Electron Packager API](https://github.com/electron/electron-packager/blob/master/docs/api.md#darwindarkmodesupport)
486+
for more details.

filenames.auto.gni

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ auto_filenames = {
3434
"docs/api/menu.md",
3535
"docs/api/modernization",
3636
"docs/api/native-image.md",
37+
"docs/api/native-theme.md",
3738
"docs/api/net-log.md",
3839
"docs/api/net.md",
3940
"docs/api/notification.md",
@@ -131,6 +132,7 @@ auto_filenames = {
131132
"lib/common/api/deprecate.ts",
132133
"lib/common/api/module-list.js",
133134
"lib/common/api/native-image.js",
135+
"lib/common/api/native-theme.ts",
134136
"lib/common/api/shell.js",
135137
"lib/common/buffer-utils.ts",
136138
"lib/common/clipboard-utils.ts",
@@ -229,7 +231,7 @@ auto_filenames = {
229231
"lib/browser/api/protocol.ts",
230232
"lib/browser/api/screen.ts",
231233
"lib/browser/api/session.js",
232-
"lib/browser/api/system-preferences.js",
234+
"lib/browser/api/system-preferences.ts",
233235
"lib/browser/api/top-level-window.js",
234236
"lib/browser/api/touch-bar.js",
235237
"lib/browser/api/tray.js",
@@ -262,6 +264,7 @@ auto_filenames = {
262264
"lib/common/api/exports/electron.js",
263265
"lib/common/api/module-list.js",
264266
"lib/common/api/native-image.js",
267+
"lib/common/api/native-theme.ts",
265268
"lib/common/api/shell.js",
266269
"lib/common/buffer-utils.ts",
267270
"lib/common/clipboard-utils.ts",
@@ -287,6 +290,7 @@ auto_filenames = {
287290
"lib/common/api/exports/electron.js",
288291
"lib/common/api/module-list.js",
289292
"lib/common/api/native-image.js",
293+
"lib/common/api/native-theme.ts",
290294
"lib/common/api/shell.js",
291295
"lib/common/buffer-utils.ts",
292296
"lib/common/clipboard-utils.ts",
@@ -337,6 +341,7 @@ auto_filenames = {
337341
"lib/common/api/exports/electron.js",
338342
"lib/common/api/module-list.js",
339343
"lib/common/api/native-image.js",
344+
"lib/common/api/native-theme.ts",
340345
"lib/common/api/shell.js",
341346
"lib/common/buffer-utils.ts",
342347
"lib/common/clipboard-utils.ts",

filenames.gni

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,8 @@ filenames = {
419419
"shell/common/api/atom_api_native_image.cc",
420420
"shell/common/api/atom_api_native_image.h",
421421
"shell/common/api/atom_api_native_image_mac.mm",
422+
"shell/common/api/atom_api_native_theme.cc",
423+
"shell/common/api/atom_api_native_theme.h",
422424
"shell/common/api/atom_api_shell.cc",
423425
"shell/common/api/atom_api_v8_util.cc",
424426
"shell/common/api/electron_bindings.cc",

lib/browser/api/system-preferences.js

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { EventEmitter } from 'events'
2+
import { deprecate } from 'electron'
3+
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences')
4+
5+
// SystemPreferences is an EventEmitter.
6+
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype)
7+
EventEmitter.call(systemPreferences)
8+
9+
if ('appLevelAppearance' in systemPreferences) {
10+
deprecate.fnToProperty(
11+
SystemPreferences.prototype,
12+
'appLevelAppearance',
13+
'_getAppLevelAppearance',
14+
'_setAppLevelAppearance'
15+
)
16+
}
17+
18+
if ('effectiveAppearance' in systemPreferences) {
19+
deprecate.fnToProperty(
20+
SystemPreferences.prototype,
21+
'effectiveAppearance',
22+
'_getEffectiveAppearance'
23+
)
24+
}
25+
26+
SystemPreferences.prototype.isDarkMode = deprecate.moveAPI(
27+
SystemPreferences.prototype.isDarkMode,
28+
'systemPreferences.isDarkMode()',
29+
'nativeTheme.shouldUseDarkColors'
30+
)
31+
SystemPreferences.prototype.isInvertedColorScheme = deprecate.moveAPI(
32+
SystemPreferences.prototype.isInvertedColorScheme,
33+
'systemPreferences.isInvertedColorScheme()',
34+
'nativeTheme.shouldUseInvertedColorScheme'
35+
)
36+
SystemPreferences.prototype.isHighContrastColorScheme = deprecate.moveAPI(
37+
SystemPreferences.prototype.isHighContrastColorScheme,
38+
'systemPreferences.isHighContrastColorScheme()',
39+
'nativeTheme.shouldUseHighContrastColors'
40+
)
41+
42+
module.exports = systemPreferences

lib/common/api/deprecate.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,15 @@ const deprecate: ElectronInternal.DeprecationUtil = {
5151
const warn = warnOnce(`${fn.name} function`, `${newName} function`)
5252
return function (this: any) {
5353
warn()
54-
fn.apply(this, arguments)
54+
return fn.apply(this, arguments)
55+
}
56+
},
57+
58+
moveAPI: (fn: Function, oldUsage: string, newUsage: string) => {
59+
const warn = warnOnce(oldUsage, newUsage)
60+
return function (this: any) {
61+
warn()
62+
return fn.apply(this, arguments)
5563
}
5664
},
5765

@@ -69,7 +77,7 @@ const deprecate: ElectronInternal.DeprecationUtil = {
6977
},
7078

7179
// deprecate a getter/setter function pair in favor of a property
72-
fnToProperty: (prototype: any, prop: string, getter: string, setter: string) => {
80+
fnToProperty: (prototype: any, prop: string, getter: string, setter?: string) => {
7381
const withWarnOnce = function (obj: any, key: any, oldName: string, newName: string) {
7482
const warn = warnOnce(oldName, newName)
7583
const method = obj[key]
@@ -80,7 +88,9 @@ const deprecate: ElectronInternal.DeprecationUtil = {
8088
}
8189

8290
prototype[getter.substr(1)] = withWarnOnce(prototype, getter, `${getter.substr(1)} function`, `${prop} property`)
83-
prototype[setter.substr(1)] = withWarnOnce(prototype, setter, `${setter.substr(1)} function`, `${prop} property`)
91+
if (setter) {
92+
prototype[setter.substr(1)] = withWarnOnce(prototype, setter, `${setter.substr(1)} function`, `${prop} property`)
93+
}
8494
},
8595

8696
// remove a property with no replacement

lib/common/api/module-list.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
module.exports = [
55
{ name: 'clipboard', loader: () => require('./clipboard') },
66
{ name: 'nativeImage', loader: () => require('./native-image') },
7+
{ name: 'nativeTheme', loader: () => require('./native-theme') },
78
{ name: 'shell', loader: () => require('./shell') },
89
// The internal modules, invisible unless you know their names.
910
{ name: 'deprecate', loader: () => require('./deprecate'), private: true }

lib/common/api/native-theme.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { EventEmitter } from 'events'
2+
3+
const { NativeTheme, nativeTheme } = process.electronBinding('native_theme')
4+
5+
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype)
6+
EventEmitter.call(nativeTheme as any)
7+
8+
module.exports = nativeTheme

shell/browser/api/atom_api_system_preferences.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ void SystemPreferences::BuildPrototype(
9999
.SetMethod("removeUserDefault", &SystemPreferences::RemoveUserDefault)
100100
.SetMethod("isSwipeTrackingFromScrollEventsEnabled",
101101
&SystemPreferences::IsSwipeTrackingFromScrollEventsEnabled)
102-
.SetMethod("getEffectiveAppearance",
102+
.SetMethod("_getEffectiveAppearance",
103103
&SystemPreferences::GetEffectiveAppearance)
104104
.SetMethod("_getAppLevelAppearance",
105105
&SystemPreferences::GetAppLevelAppearance)
@@ -108,6 +108,8 @@ void SystemPreferences::BuildPrototype(
108108
.SetProperty("appLevelAppearance",
109109
&SystemPreferences::GetAppLevelAppearance,
110110
&SystemPreferences::SetAppLevelAppearance)
111+
.SetProperty("effectiveAppearance",
112+
&SystemPreferences::GetEffectiveAppearance)
111113
.SetMethod("getSystemColor", &SystemPreferences::GetSystemColor)
112114
.SetMethod("canPromptTouchID", &SystemPreferences::CanPromptTouchID)
113115
.SetMethod("promptTouchID", &SystemPreferences::PromptTouchID)

0 commit comments

Comments
 (0)