3. Menu and system tray in electron

Get into the habit of writing together! This is the 7th day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .

Menu and Tray

I believe that everyone has used many desktop applications in daily life, such as WeChat, QQ, NetEase Cloud Music, etc. These applications have some things in common, for example, there are menus on the page, icons on the system tray, right click The icon on the tray also has a small menu, etc. Today, let's take a look at the menu and the system tray to see how to achieve it.

implement menu

In the previous articles, we built a simple hello world example, and this chapter adds menus on the basis of the previous ones.

Create a new menu.js in the directory with the following content:

const { Menu, BrowserWindow } = require('electron')
// 构建菜单结构
const template = [
  {
    label: 'parent-1',
    // submenu 代表子菜单
    submenu: [
      { 
          label: '创建新窗口' ,
          accelerator: 'ctrl+n', // 菜单的快捷方式
          click: () => {
            // 创建一个新的窗口
            let sonWin = new BrowserWindow({
              width: 200,
              height: 200,
            })
            sonWin.loadFile('./index2.html')
            // 为关闭的时候进行清空
            sonWin.on('close', () => {
              sonWin = null
            })
          }
      },
      { label: 'child-2' },
      { label: 'child-3' }
    ],
  },
  {
    label: 'parent-2',
    submenu: [
      { label: 'child-1' },
      { label: 'child-2' }
    ],
  },
]

// 在模板中创建菜单
const myMenu = Menu.buildFromTemplate(template)
// 为应用程序设置菜单
Menu.setApplicationMenu(myMenu)
复制代码

Introduced in index.js:

// 引入menu
require('./menu')
复制代码

In the above example, some menus are added through the built-in Menu of electron, and shortcut keys can be bound at the same time, and the click event of the menu can be bound. The effect is as follows:

20220410_105558.gifBut in the current desktop applications, many of them hide the built-in menus, and then customize to develop the menus. How to hide them!

There are three methods as follows:

方法1const electron = require('electron')
const Menu = electron.Menu
function createWindow (){
    Menu.setApplicationMenu(null)
}

方法2new BrowserWindow({autoHideMenuBar: true})

方法3new BrowserWindow({frame: false})
复制代码

image.png

Careful friends may find that the window cannot be dragged and dropped, and drag and drop can be achieved by adding css:

body{ -webkit-app-region: drag;} // 可拖拽
body{ -webkit-app-region: no-drag;} // 禁止拖拽
复制代码

For more usage methods, please refer to the official documentation www.electronjs.org/zh/docs/lat…

Add Tray

image.pngEveryone has seen the system tray, but how is it implemented in electron? First, we need to prepare an icon, then create a new tray.js in the directory, and directly add the code:

// tray.js

const { app, Menu, Tray, nativeImage } = require('electron');
const path = require("path");

class HTray {
  constructor() {
    this.tray = null;
    this.normalImage = nativeImage.createFromPath(path.join(__dirname, "./build/icons/icon.ico")); // 托盘图标
  }

  createMenu() {
    const trayMenuTemplate = [
      {
        label: "显示/隐藏",
        click: function() {
          return app.win.isVisible() ? app.win.hide() : app.win.show();
        }
      },
      {
        label: "退出",
        click: function() {
          app.quit();
        }
      }
    ];
    const contextMenu = Menu.buildFromTemplate(trayMenuTemplate);
    this.tray.setToolTip("前端大狗");
    this.tray.setContextMenu(contextMenu);
  }

  show() {
    this.tray = new Tray(this.normalImage);
    this.createMenu();
    this.tray.on("click", () => {
      this.stopFlash();
      this.openWindow();
    });
  }

  openWindow() {
    app.win.isVisible() ? app.win.hide() : app.win.show();
    app.win.setSkipTaskbar(false);
  }
}

module.exports.HTray = HTray;
复制代码

For the convenience of use, the method of the tray is put into the class here, and the right-click menu and the click method of the menu are added, and the effect will be demonstrated below!

Friends who have used the computer version of WeChat should know that when you click the tray icon, WeChat will be displayed, and when you click it, it will be hidden, and the new message icon will flash. Let’s also implement it:

//tray.js

const { app, Menu, Tray, nativeImage } = require('electron');
const path = require("path");

class HTray {
  constructor() {
    this.tray = null;
    this.timer = null;
    this.normalImage = nativeImage.createFromPath(path.join(__dirname, "./build/icons/icon.ico")); // 托盘图标
    this.maskImage = nativeImage.createFromPath(path.join(__dirname, "./build/icons/empty.png")); // 空白图标,供闪烁使用
  }

  createMenu() {
    const trayMenuTemplate = [
      {
        label: "显示/隐藏",
        click: function() {
          return app.win.isVisible() ? app.win.hide() : app.win.show();
        }
      },
      {
        label: "退出",
        click: function() {
          app.quit();
        }
      }
    ];
    const contextMenu = Menu.buildFromTemplate(trayMenuTemplate);
    this.tray.setToolTip("前端大狗");
    this.tray.setContextMenu(contextMenu);
  }

  show() {
    this.tray = new Tray(this.normalImage);
    this.createMenu();
    this.tray.on("click", () => {
      this.stopFlash();
      this.openWindow();
    });
  }

  openWindow() {
    app.win.isVisible() ? app.win.hide() : app.win.show();
    app.win.setSkipTaskbar(false);
  }

  // 图标闪烁
  startFlash() {
    // 托盘闪烁
    let count = 0;
    this.timer = setInterval(() => {
      try {
        count++;
        if (count % 2 === 0) {
          this.tray.setImage(this.normalImage);
        } else {
          this.tray.setImage(this.maskImage);
        }
        if (count === 100) {
          count = 0;
        }
      } catch (err) {
        console.log(err);
        this.stopFlash();
      }
    }, 700);
  }

  // 停止闪烁
  stopFlash() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
    this.tray.setImage(this.normalImage);
  }
}

module.exports.HTray = HTray;
复制代码

The operation that triggers the flicker is triggered during the rendering process, so you need to add the following code to index.html;

<body>
  <div>闪烁测试</div>

  <button onclick="start()">开始闪烁</button>
  <button onclick="stop()">停止闪烁</button>

  <script>
    // ----------此处可忽略ipcRenderer, 后续章节会着重讲这里
    const { ipcRenderer } = require('electron');
    // 点击闪烁
    function start() {
      ipcRenderer.send("startFlash");
    }

    // 停止闪烁
    function stop() {
      ipcRenderer.send("stopFlash");
    }
  </script>
</body>
复制代码

When the rendering process sends a signal, it needs to be received in the main process:

// index.js

// 引入
const HTray = require('./tray').HTray

// 创建系统托盘
app.tray = new HTray();
// 显示系统托盘
app.tray.show();

// 托盘闪烁
ipcMain.on('startFlash', (event, arg) => {
    app.tray.startFlash();
})

// 停止闪烁
ipcMain.on('stopFlash', (event, arg) => {
    app.tray.stopFlash();
})

// 监听关闭事件
app.win.on('close', (event) => {
    // 1.隐藏窗口
    app.win.hide(); 
    // 2.隐藏任务栏
    app.win.setSkipTaskbar(true);
    // 3.阻止默认行为(因为并不是要关闭)
    event.preventDefault();
})
复制代码

The principle is to achieve the flickering effect by switching pictures! ! ! ! Is not it simple! ! ! ! So far, the basic function of the tray is over, see the effect

20220410_113743.gif

For more functions, please refer to the official documentation! www.electronjs.org/docs/latest…

grateful

Thank you for reading this article, I hope it can be helpful to you, and if you have any questions, please correct me.

I'm Nicnic, if you think it's good to write, please give it a like ❤.

Writing is not easy, "Like" + "Watching" + "Retweet" Thank you for your support ❤

Good articles in the past

"Javascript high-frequency handwritten questions 1.0"

"A small component for generating image verification code in Vue"

"Front-end JS high-frequency interview questions---1. Publish-subscribe model"

"Front-end JS high-frequency interview questions --- 2. Singleton mode"

"Front-end JS high-frequency interview questions---3. Agency mode"

"Front-end JS high-frequency interview questions---4. Strategy mode"

"Front-end CSS high-frequency interview questions --- 1. CSS selectors, priorities, and inheritance properties"

"Front-end CSS high-frequency interview questions---2. The difference between em/px/rem/vh/vw"

"Front-end CSS high-frequency interview questions --- 3. How to implement a two-column layout, the right side is adaptive? What about the three-column layout in the middle?

Guess you like

Origin juejin.im/post/7084816310739140615