Electron Dark Mode

2023-02-16 17:15 更新

概览

自动更新原生界面

"本地界面"包括文件选择器、窗口边框、对话框、上下文 菜单等 - 任何UI来自操作系统而非应用的界面。 默认行为是从操作系统选择自动主题。

自动更新您自己的界面

如果您的应用有自己的黑暗模式,您应该在与系统黑暗模式设置同步时切换。 你可以通过 prefers-color-scheme CSS 媒体查询来实现此功能.

手动更新您自己的界面

如果你想在亮/暗模式之间手动切换,你可以通过在 nativeTheme 模块的 themeSource 属性中设置所需的模式来实现。此属性的值将传播到您的渲染器进程。任何与 prefers-color-scheme 相关的 CSS 规则都将相应更新。

macOS 设置

在 macOS 10.14 Mojave 中, Apple 为所有 macOS 电脑引入了一个全新的 系统级暗色模式。 如果您的 Electron 应用具有深色模式,您可以 使用"本机 api" 应用。

在 macOS 10.15 Catalina 中,Apple 为所有 macOS 计算机引入了一个新的“自动”暗模式选项。为了让API isDarkMode 和 Tray 在这个模式中正常工作,你需要在 Info.plist 文件里把 NSRequiresAquaSystemAppearance 设置为 false ,或者使用 >=7.0.0 的Electron。 Electron Packager 和 Electron Forge 都有一个 darwinDarkModeSupport 选项,可以在应用程序构建期间自动更改 Info.plist。

如果您希望在使用 Electron > 8.0.0 时选择退出,则必须将 Info.plist 文件中的 NSRequiresAquaSystemAppearance 键设置为 true。请注意,由于使用 macOS 10.14 SDK,Electron 8.0.0 及更高版本不会让您选择退出此主题。

示例

此示例演示了Electron 应用程序从nativeTheme中获取主题颜色。 此外,它还使用 IPC 通道提供主题切换和重置控制。

 main.js preload.js  index.html  renderer.js  style.css 
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()
  }
})
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('darkMode', {
  toggle: () => ipcRenderer.invoke('dark-mode:toggle'),
  system: () => ipcRenderer.invoke('dark-mode:system')
})
<!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>
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'
})
@media (prefers-color-scheme: dark) {
  body { background: #333; color: white; }
}

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

DOCS/FIDDLES/FEATURES/MACOS-DARK-MODE (22.0.2)

Open in Fiddle

它是如何工作的呢?

从 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>

以及 styles.css 文件:

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

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

该示例渲染一个包含几个元素的 HTML 页面。 <strong id="theme-source"> 元素显示当前选中的主题,两个 <button> 元素是 控件。 CSS 文件使用 prefers-color-scheme 媒体查询 设置 <body> 元素背景和文本颜色。

preload.js 脚本在 window对象中添加了一个新的 API叫做 深色模式。 此 API 暴露两个IPC 通道到渲染器进程,分别为 'dark-mode:toggle' 和 'dark-mode:system'。 它还分配了两个方法, toggle 和 system,它们将渲染器中的信息传递到 主进程。

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

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

现在,渲染器进程可以安全地与主进程通信,并对nativeTheme 对象执行必要的变更。

renderer.js 文件负责控制 <button> 功能。

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'
})

使用 addEventListener, renderer.js 文件将'click' 事件监听器添加到每个按钮元素上。 每个事件监听处理器都会调用到相关的 window.darkmode 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()
  }
})

ipcMain.handle 方法表明主进程如何响应来自 HTML 页面上 按钮的点击事件。

'dark-mode:toggle' IPC 通道处理器方法检查 shouldUseDarkColors boolean属性 设置对应的 themeSource, 然后返回当前的 shouldUseDarkColors 属性。 回顾此 IPC 通道的渲染器进程事件监听器,此处理器的返回值为 <strong id='theme-source'> 元素指定正确的文本。

'dark-mode:system' IPC 通道处理器方法将字符串 'system' 赋值到 themeSource 同时无返回值。 这也对应于相应的渲染器进程事件监听器,因为方法正在等待,且不需要返回值。

使用Electron Fiddle运行示例,然后点击“切换深色模式”按钮; 应用程序应该开始在亮色和黑色背景颜色之间交替。


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号