Bootstrap

第6章:Electron的菜单与托盘(1)

6.1 应用菜单

应用菜单通常位于窗口顶部,用于提供应用的主要功能选项。我们使用 Menu 模块来创建和管理应用菜单。

6.1.1 创建应用菜单

主进程代码

const { app, BrowserWindow, Menu } = require('electron');
const path = require('path');

let mainWindow;

const createMainWindow = () => {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false
    }
  });

  mainWindow.loadFile('index.html');
  mainWindow.webContents.openDevTools();
  mainWindow.on('closed', () => {
    mainWindow = null;
  });

  // 创建应用菜单
  const menu = Menu.buildFromTemplate([
    {
      label: 'File',
      submenu: [
        {
          label: 'Open',
          click: () => {
            console.log('Open clicked');
          }
        },
        {
          label: 'Save',
          click: () => {
            console.log('Save clicked');
          }
        },
        { type: 'separator' },
        {
          label: 'Exit',
          click: () => {
            app.quit();
          }
        }
      ]
    },
    {
      label: 'Edit',
      submenu: [
        { role: 'undo' },
        { role: 'redo' },
        { type: 'separator' },
        { role: 'cut' },
        { role: 'copy' },
        { role: 'paste' }
      ]
    }
  ]);

  // 设置应用菜单
  Menu.setApplicationMenu(menu);
};

app.on('ready', createMainWindow);
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});
app.on('activate', () => {
  if (mainWindow === null) {
    createMainWindow();
  }
});

6.2 上下文菜单

上下文菜单是当用户右键点击某个元素时显示的菜单。我们使用 Menu 模块来创建和管理上下文菜单。

6.2.1 创建上下文菜单

主进程代码

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

let mainWindow;

const createMainWindow = () => {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false
    }
  });

  mainWindow.loadFile('index.html');
  mainWindow.webContents.openDevTools();
  mainWindow.on('closed', () => {
    mainWindow = null;
  });

  // 创建上下文菜单
  const contextMenu = Menu.buildFromTemplate([
    {
      label: 'Cut',
      role: 'cut'
    },
    {
      label: 'Copy',
      role: 'copy'
    },
    {
      label: 'Paste',
      role: 'paste'
    },
    { type: 'separator' },
    {
      label: 'Custom Action',
      click: () => {
        console.log('Custom action triggered');
      }
    }
  ]);

  // 监听渲染进程的上下文菜单事件
  ipcMain.on('show-context-menu', (event) => {
    const win = BrowserWindow.fromWebContents(event.sender);
    contextMenu.popup(win);
  });
};

app.on('ready', createMainWindow);
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});
app.on('activate', () => {
  if (mainWindow === null) {
    createMainWindow();
  }
});

渲染进程代码
Html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Context Menu Example</title>
    <script src="./renderer.js"></script>
  </head>
  <body>
    <h1>Right-click to see context menu</h1>
  </body>
</html>

renderer.js

// renderer.js
document.addEventListener("contextmenu", (e) => {
  e.preventDefault();
  window.api.send("show-context-menu");
});

preload.js

// preload.js
const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld("api", {
  send: (channel, data) => {
    // White-listing channels
    if (channel === "show-context-menu") {
      ipcRenderer.send(channel, data);
    }
  },
});
;