Skip to main content

Dark Mode暗模式

Overview概述

Automatically update the native interfaces自动更新本机接口

"Native interfaces" include the file picker, window border, dialogs, context menus, and more - anything where the UI comes from your operating system and not from your app. “本机界面”包括文件选择器、窗口边框、对话框、上下文菜单等-任何UI来自操作系统而非应用程序的界面。The default behavior is to opt into this automatic theming from the OS.默认行为是从操作系统选择自动主题化。

Automatically update your own interfaces自动更新您自己的界面

If your app has its own dark mode, you should toggle it on and off in sync with the system's dark mode setting. 如果您的应用程序有自己的暗模式,则应与系统的暗模式设置同步打开和关闭。You can do this by using the [prefer-color-scheme] CSS media query.您可以使用[Prefere color scheme]CSS媒体查询来实现这一点。

Manually update your own interfaces手动更新您自己的界面

If you want to manually switch between light/dark modes, you can do this by setting the desired mode in the themeSource property of the nativeTheme module. 如果要手动切换亮/暗模式,可以通过在nativeTheme模块的themeSource属性中设置所需模式来实现。This property's value will be propagated to your Renderer process. 此属性的值将传播到渲染器进程。Any CSS rules related to prefers-color-scheme will be updated accordingly.任何与首选颜色方案相关的CSS规则都将相应更新。

macOS settingsmacOS设置

In macOS 10.14 Mojave, Apple introduced a new system-wide dark mode for all macOS computers. 在macOS 10.14 Mojave中,苹果为所有macOS计算机引入了新的全系统暗模式If your Electron app has a dark mode, you can make it follow the system-wide dark mode setting using the nativeTheme api.如果您的Electron应用程序具有暗模式,您可以使用nativeTheme api使其遵循系统范围的暗模式设置。

In macOS 10.15 Catalina, Apple introduced a new "automatic" dark mode option for all macOS computers. 在macOS 10.15 Catalina中,苹果为所有macOS计算机引入了新的“自动”暗模式选项。In order for the nativeTheme.shouldUseDarkColors and Tray APIs to work correctly in this mode on Catalina, you need to use Electron >=7.0.0, or set NSRequiresAquaSystemAppearance to false in your Info.plist file for older versions. 为了使nativeTheme.shouldUseDarkColorsTrayAPI在此模式下在Catalina上正确工作,您需要使用Electron>=7.0.0,或者在旧版本的Info.plist文件中将NSRequiresAquaSystemAppearance设置为falseBoth Electron Packager and Electron Forge have a darwinDarkModeSupport option to automate the Info.plist changes during app build time.Electron PackagerElectron Forge都有一个darwinDarkModeSupport选项,可以在应用程序构建期间自动更改Info.plist

If you wish to opt-out while using Electron > 8.0.0, you must set the NSRequiresAquaSystemAppearance key in the Info.plist file to true. 如果希望在使用Electron>8.0.0时选择退出,则必须将Info.plist文件中的NSRequiresAquaSystemAppearance键设置为truePlease note that Electron 8.0.0 and above will not let you opt-out of this theming, due to the use of the macOS 10.14 SDK.请注意,由于使用了macOS 10.14 SDK,Electron 8.0.0及以上版本不允许您选择退出此主题。

Example示例

This example demonstrates an Electron application that derives its theme colors from the nativeTheme. 此示例演示了一个从nativeTheme派生主题颜色的Electron应用程序。Additionally, it provides theme toggle and reset controls using IPC channels.此外,它还提供使用IPC通道的主题切换和重置控件。

const { app, BrowserWindow, ipcMain, nativeTheme } = require('electron')
const path = require('path')

function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

win.loadFile('index.html')

ipcMain.handle('dark-mode:toggle', () => {
if (nativeTheme.shouldUseDarkColors) {
nativeTheme.themeSource = 'light'
} else {
nativeTheme.themeSource = 'dark'
}
return nativeTheme.shouldUseDarkColors
})

ipcMain.handle('dark-mode:system', () => {
nativeTheme.themeSource = 'system'
})
}

app.whenReady().then(() => {
createWindow()

app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})

How does this work?这是如何起作用的?

Starting with the index.html file:index.html文件开始:

index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
<link rel="stylesheet" type="text/css" href="./styles.css">
</head>
<body>
<h1>Hello World!</h1>
<p>Current theme source: <strong id="theme-source">System</strong></p>

<button id="toggle-dark-mode">Toggle Dark Mode</button>
<button id="reset-to-system">Reset to System Theme</button>

<script src="renderer.js"></script>
</body>
</body>
</html>

And the styles.css file:以及styles.css文件:

styles.css
@media (prefers-color-scheme: dark) {
body { background: #333; color: white; }
}

@media (prefers-color-scheme: light) {
body { background: #ddd; color: black; }
}

The example renders an HTML page with a couple elements. 该示例呈现了一个包含两个元素的HTML页面。The <strong id="theme-source"> element shows which theme is currently selected, and the two <button> elements are the controls. <strong id="theme-source">元素显示当前选择的主题,两个<button>元素是控件。The CSS file uses the prefers-color-scheme media query to set the <body> element background and text colors.CSS文件使用首选颜色方案媒体查询来设置<body>元素背景和文本颜色。

The preload.js script adds a new API to the window object called darkMode. preload.js脚本向名为darkModewindow对象添加了一个新的API。This API exposes two IPC channels to the renderer process, 'dark-mode:toggle' and 'dark-mode:system'. 该API向渲染器进程公开了两个IPC通道'dark-mode:toggle''dark-mode:system'It also assigns two methods, toggle and system, which pass messages from the renderer to the main process.它还分配了两个方法:togglesystem,它们将消息从渲染器传递到主进程。

preload.js
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('darkMode', {
toggle: () => ipcRenderer.invoke('dark-mode:toggle'),
system: () => ipcRenderer.invoke('dark-mode:system')
})

Now the renderer process can communicate with the main process securely and perform the necessary mutations to the nativeTheme object.现在,渲染器进程可以安全地与主进程通信,并对nativeTheme对象执行必要的修改。

The renderer.js file is responsible for controlling the <button> functionality.renderer.js文件负责控制<button>功能。

renderer.js
document.getElementById('toggle-dark-mode').addEventListener('click', async () => {
const isDarkMode = await window.darkMode.toggle()
document.getElementById('theme-source').innerHTML = isDarkMode ? 'Dark' : 'Light'
})

document.getElementById('reset-to-system').addEventListener('click', async () => {
await window.darkMode.system()
document.getElementById('theme-source').innerHTML = 'System'
})

Using addEventListener, the renderer.js file adds 'click' event listeners to each button element. 使用addEventListenerrenderer.js文件将'click'事件监听器添加到每个按钮元素。Each event listener handler makes calls to the respective window.darkMode API methods.每个事件侦听器处理程序都调用相应的window.darkMode API方法。

Finally, the main.js file represents the main process and contains the actual nativeTheme API.最后,main.js文件表示主进程并包含实际的nativeTheme API。

const { app, BrowserWindow, ipcMain, nativeTheme } = require('electron')
const path = require('path')

const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

win.loadFile('index.html')

ipcMain.handle('dark-mode:toggle', () => {
if (nativeTheme.shouldUseDarkColors) {
nativeTheme.themeSource = 'light'
} else {
nativeTheme.themeSource = 'dark'
}
return nativeTheme.shouldUseDarkColors
})

ipcMain.handle('dark-mode:system', () => {
nativeTheme.themeSource = 'system'
})
}

app.whenReady().then(() => {
createWindow()

app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})

The ipcMain.handle methods are how the main process responds to the click events from the buttons on the HTML page.ipcMain.handle方法是主进程如何响应HTML页面上按钮的点击事件。

The 'dark-mode:toggle' IPC channel handler method checks the shouldUseDarkColors boolean property, sets the corresponding themeSource, and then returns the current shouldUseDarkColors property. 'dark-mode:toggle'IPC通道处理程序方法检查shouldUseDarkColors布尔属性,设置相应的themeSource,然后返回当前的shouldUseDarkColors属性。Looking back on the renderer process event listener for this IPC channel, the return value from this handler is utilized to assign the correct text to the <strong id='theme-source'> element.回顾此IPC通道的渲染器进程事件监听器,该处理程序的返回值用于将正确的文本分配给<strong id='theme-source'>元素。

The 'dark-mode:system' IPC channel handler method assigns the string 'system' to the themeSource and returns nothing. 'dark-mode:system'IPC通道处理程序方法将字符串'system'分配给themeSource,但不返回任何内容。This also corresponds with the relative renderer process event listener as the method is awaited with no return value expected.这也对应于相对渲染器进程事件侦听器,因为该方法在等待时没有预期的返回值。

Run the example using Electron Fiddle and then click the "Toggle Dark Mode" button; the app should start alternating between a light and dark background color.使用Electron Fiddle运行示例,然后单击“切换暗模式”按钮;应用程序应开始在浅色和深色背景色之间交替。

Dark Mode