How to customize a system menu bar with electron height?

background

Recently I was working on a real-time chatting PC client, and encountered such a task. When the client receives messages from other users, it should flash the system tray icon, and display the menu bar of unread messages when the mouse moves to the system tray ( Yes, it is a message prompt similar to QQ, such as the picture below); here to add, we choose electron as our development framework, for us, electron can use front-end language (HTML+CSS+JS), and it can be cross-platform The framework is our best choice.

How to customize a system menu bar with electron height?

Problem solving ideas

1. Normal people’s thinking is to first check whether electron has integrated api. I am no exception, so I looked around and found a system tray Tray module. The approximate api template looks like this:

const { app, Menu, Tray } = require('electron')
let tray = null;
let openVoice = true;
app.whenReady().then(() => {
  tray = new Tray('/path/to/my/icon');
  const contextMenu = Menu.buildFromTemplate([
    {
        label: openVoice ? '关闭声音' : '开启声音',
        icon: openVoice ? "trayMenu/notVoice.png": "trayMenu/voice.png",
        click: (event: any) => {
            openVoice = !openVoice;
        },
    },
    {
        label: '退出',
        icon:  "trayMenu/exit.png",
        click: () => {
            app.exit(0);
        }
    }
  ]);
  tray.setToolTip('This is my application.')
  tray.setContextMenu(contextMenu)
})

The actual effect is probably like this:
How to customize a system menu bar with electron height?

Generally speaking, it supports more general things such as titles, icons, sub-menus, and click callbacks. It seems that there is a big gap from our ideals. If I don’t believe in evil, I would like to try if I can support rendering HTML, so I just add contextMenu One more sub item:

{
    label: '<p style="backgroud: #red">测试</p>',
    icon:  "trayMenu/exit.png",
    click: () => {
        app.exit(0);
    }
}

The result is also more touching, it is exactly the output I wrote, it seems that this way is not working;

2. Customize a system menu bar by yourself

According to our previous point of practice, we can basically see that the system tray Tray is basically useless for us except to create a tray icon; therefore, I thought of using a browser window (BrowserWindow) as the menu bar, so that the menu bar should be made What it looks like is completely in our hands, but we must first solve the following problems:

1. How to monitor the mouse sliding to our tray icon

We first create the corresponding menu bar window, which is hidden by default

menuWin = new BrowserWindow({
    modal: true,
    autoHideMenuBar: true,
    disableAutoHideCursor: true, 
    frame: false,
    show: false,
});
menuWin.loadURL(config.frontUrl + '/#/newMessage'); // 加载对应的菜单栏页面

I looked for the tray API, and only found a mouse-move event to deal with this problem, but this event is only triggered when it slides into the tray, and there is no corresponding event to draw out the tray, so we have to do one Mechanism to determine whether the mouse slides out of the tray, see the following code for details;
How to customize a system menu bar with electron height?

import { app, Tray, Menu, nativeImage, screen, BrowserWindow,BrowserView,shell } from "electron";
let isLeave = true; // 存储鼠标是否离开托盘的状态
tray.on('mouse-move', (event: any,point: any) => {
    if( isLeave == true ) { // 从其他地方第一次移入菜单时,开始显示菜单页,然后在菜单内移动时不重复再显示菜单
        menuWin.show();
    }
    isLeave = false;
    checkTrayLeave();
});
/**
 * 检查鼠标是否从托盘离开
 */
checkTrayLeave() {
    clearInterval(this.leaveInter)
    this.leaveInter = setInterval(() => {
        let trayBounds = tray.getBounds();
        let point = screen.getCursorScreenPoint();
        // 判断是否再托盘内
        if(!(trayBounds.x < point.x && trayBounds.y < point.y && point.x < (trayBounds.x + trayBounds.width) && point.y < (trayBounds.y  + trayBounds.height))){
            // 触发 mouse-leave
            clearInterval(this.leaveInter);
            menuWin.hide(); // 隐藏菜单栏
            isLeave = true;
        } else {
            console.log('isOn');
        }
    }, 100)
},

2. How to control the position of the window

According to the code in the previous step, tray.getBounds() can be used to obtain the position information of the tray icon. Let’s first assume that the height and width of our menu bar are both 200

let trayBounds = this.appTray.getBounds();
if(!params.x) {
    params.x = trayBounds.x - ( 200 / 2)
}
if(!params.y) {
    params.y = trayBounds.y - params.height;
}
this.menuWin.setBounds(params);

When this step is done, basically when the mouse slides in and out of our tray icon, it controls the display and hiding of the menu bar; but we will find a problem, when we slide the mouse to the window of the menu bar, it will be hidden The menu bar, you can't do anything like this. Therefore, when we identify the tray position in the first step, we must draw the entire menu bar window into the normal range of our system tray sliding:

/**
 * 检查鼠标是否从托盘离开
 */
checkTrayLeave() {
    clearInterval(this.leaveInter)
    this.leaveInter = setInterval(() => {
        let trayBounds = tray.getBounds();
        let point = screen.getCursorScreenPoint();
        // 判断是否再托盘内
        if(!(trayBounds.x < point.x && trayBounds.y < point.y && point.x < (trayBounds.x + trayBounds.width) && point.y < (trayBounds.y  + trayBounds.height))){
             // 判断是否在弹出菜单内
             let menuBounds = this.menuWin.getBounds()
             if(menuBounds.x < point.x && menuBounds.y < point.y && point.x < (menuBounds.x + menuBounds.width) && point.y < (menuBounds.y  + menuBounds.height)) {
                 console.log('isOnMenupage');
                 return ;
             }
             // 触发 mouse-leave
             clearInterval(this.leaveInter);
             menuWin.hide(); // 隐藏菜单栏
             isLeave = true;
        } else {
            console.log('isOn');
        }
    }, 100)
},

to sum up

Basically, the entire scheme of customizing the system menu bar is probably such a process, but this scheme is also limited to windows and mac systems, compatibility on Linux really can’t be done, most APIs do not support Linux systems.

Guess you like

Origin blog.51cto.com/15024210/2582577