当前位置: 首页 > news >正文

Electron桌面应用开发:自定义菜单

    完成初始应用的创建Electron桌面应用开发:创建应用,随后我们就可以自定义软件的菜单了。菜单可以帮助用户快速找到和执行命令,而不需要记住复杂的快捷键,通过将相关功能组织在一起,用户可以更容易地发现和使用应用程序的各种特性。同时菜单允许开发者提供更多的功能选项而不必担心界面会因此变得拥挤或难以导航,比如下拉菜单弹出菜单等可以在有限的空间内提供大量的选项。
Electron的原始菜单为以下页面:
在这里插入图片描述

取消顶部菜单显示

这里可以使用两种常用的方法,第一种是在窗口创建函数中设置frame: false

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

let win = null;

function createWindow() {
    win = new BrowserWindow({
        width: 800,
        height: 600,
        frame: false,
        webPreferences: {
            nodeIntegration: true
        },
    });

    win.loadFile('index.html');

    win.on('closed', () => {
        win = null;
    });
}

app.on('ready', createWindow);

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

这样大小化和关闭按钮、标题也全部消失了,所以只适合个别情况使用。
在这里插入图片描述
第二种是在创建窗口函数中加入Menu.setApplicationMenu(null);设置,这样可以保留标题等内容:

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

let win = null;

function createWindow() {
    win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true
        },
    });

    win.loadFile('index.html');
    Menu.setApplicationMenu(null);

    win.on('closed', () => {
        win = null;
    });
}

app.on('ready', createWindow);

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

在这里插入图片描述

自定义子菜单

    这里我们可以在主文件main.js中直接添加自定义菜单的代码,也可以新建一个menu.js文件,随后在main.js中进行引用(个人推荐做法)
在Electron中定义菜单需要先引入Menu

const {Menu} = require('electron')

定义格式如下:

    const template = [
        {
            label: '菜单1',
            submenu: [
                {
                    label: '子菜单1'
                },
                {
                    label: '子菜单2',
                }
            ]
        },

        {
            label: '菜单2',
            submenu: [
                {
                    label: '子菜单1'
                },
                {
                    label: '子菜单2',
                    submenu: [
                        {
                            label: '孙子菜单1'
                        },
                        {
                            label: '孙子菜单2'
                        }
                    ]
                }
            ]
        }
    ]

可以使用click来监听事件,例如:

            {
                label: '欧耶',
                accelerator: 'CmdOrCtrl+O',
                click: () => {
                    console.log('菜单被点击了');
                }
            },

main.js中直接添加菜单代码的格式如下:

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

let win = null;

function createWindow() {
    win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true
        },
    });

    win.loadFile('index.html');

    win.on('closed', () => {
        win = null;
    });
}

const template = [
    {
        label: '菜单1',
        submenu: [
            {
                label: '子菜单1'
            },
            {
                label: '子菜单2',
            }
        ]
    },

    {
        label: '菜单2',
        submenu: [
            {
                label: '子菜单1'
            },
            {
                label: '子菜单2',
                submenu: [
                    {
                        label: '孙菜单1'
                    },
                    {
                        label: '孙菜单2'
                    }
                ]
            }
        ]
    },

    {
        label: '帮助',
        role: 'help',
        click() { require('electron').shell.openExternal('https://example.com/help') }
    }
];

const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);

app.on('ready', createWindow);

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

app.on('activate', () => {
    if (win === null) {
        createWindow();
    }
});

这里我们还是推荐使用将创建菜单的代码转移到其他文件中,比如新建一个menu.js,这样可以更加方便的进行代码编写和问题排查:
main.js:

const {app, BrowserWindow, Menu} = require('electron');
// 引入menu.js文件
const menuTemplate = require('./menu.js'); 

let win = null;

function createWindow() {
    win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true
        },
    });

    win.loadFile('index.html');

    win.on('closed', () => {
        win = null;
    });

	// 创建窗口
    const menu = Menu.buildFromTemplate(menuTemplate(win));
    Menu.setApplicationMenu(menu);
}

app.on('ready', createWindow);

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

menu.js:

const {Menu} = require('electron')

module.exports = function (win) {
    const template = [
        {
            label: '菜单1',
            submenu: [
                {
                    label: '子菜单1'
                },
                {
                    label: '子菜单2',
                }
            ]
        },

        {
            label: '菜单2',
            submenu: [
                {
                    label: '子菜单1'
                },
                {
                    label: '子菜单2',
                    submenu: [
                        {
                            label: '孙菜单1'
                        },
                        {
                            label: '孙菜单2'
                        }
                    ]
                }
            ]
        },

        {
            label: '帮助',
            role: 'help',
            click() { require('electron').shell.openExternal('https://example.com/help') }
        }
    ]
    return template;
}

两种方法的最终效果如下:
在这里插入图片描述

上下文菜单

上下文菜单通常在用户右键点击某个元素时显示,通常通过监听事件来创建和显示上下文菜单。
在主文件中需要导入ipcMain模块,main.js:

const { app, BrowserWindow, Menu, ipcMain } = require('electron');
const menuTemplate = require('./menu.js');

let win = null;

function createWindow() {
    win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false,
            preload: __dirname + './preload.js'
        },
    });

    win.loadFile('index.html');

    const menu = Menu.buildFromTemplate(menuTemplate(win));
    Menu.setApplicationMenu(menu);

    win.on('closed', () => {
        win = null;
    });
}

app.on('ready', createWindow);

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

app.on('activate', () => {
    if (win === null) {
        createWindow();
    }
});

renderer.js,DOM内容加载完成后执行的逻辑:

document.addEventListener('DOMContentLoaded', () => {
});

使用preload脚本处理上下文菜单的preload.js, 用于安全地暴露API给渲染进程:

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

contextBridge.exposeInMainWorld('electronAPI', {
    send: (channel, data) => {
         // 向主进程发送消息
        ipcRenderer.send(channel, data);
    },
    receive: (channel, func) => {
        // 接收来自主进程的消息
        ipcRenderer.on(channel, (event, ...args) => func(...args)); 
    }
});

menu.js:

const {Menu} = require('electron')

module.exports = function (win) {
    const template = [
        {
            label: '菜单1',
            submenu: [
                {
                    label: '子菜单1'
                },
                {
                    label: '子菜单2',
                }
            ]
        },

        {
            label: '菜单2',
            submenu: [
                {
                    label: '子菜单1'
                },
                {
                    label: '子菜单2',
                    submenu: [
                        {
                            label: '孙菜单1'
                        },
                        {
                            label: '孙菜单2'
                        }
                    ]
                }
            ]
        },

        {
            label: '帮助',
            role: 'help',
            click() { require('electron').shell.openExternal('https://example.com/help') }
        }
    ]
    return template;
}

index.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./style.css">
    <title>GGBond勇猛无敌</title>
</head>
<body>
    <h1>Hello GGBond</h1>

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

在这里插入图片描述

弹出式菜单

弹出式菜单可以通过编程方式手动显示,不需要特定的触发事件。
main.js:

const { app, BrowserWindow, Menu } = require('electron');
const menuTemplate = require('./menu.js'); // 引入自定义菜单模板

let win = null;

function createWindow() {
    win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false, // 在使用nodeIntegration时需要禁用contextIsolation
            preload: __dirname + '/preload.js' // 使用preload脚本处理上下文菜单
        },
    });

    win.loadFile('index.html');

    win.on('closed', () => {
        win = null;
    });

    const menu = Menu.buildFromTemplate(menuTemplate(win));
    Menu.setApplicationMenu(menu); // 设置为应用菜单
}

app.on('ready', createWindow);

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

app.on('activate', () => {
    if (win === null) {
        createWindow();
    }
});

使用preload脚本处理上下文菜单的preload.js,使用contextBridge来安全地暴露API给渲染进程,允许渲染进程调用showPopupMenu方法。:

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

contextBridge.exposeInMainWorld('electronAPI', {
    showPopupMenu: (x, y) => {
        ipcRenderer.invoke('show-popup-menu', x, y);
    }
});

renderer.js:

document.addEventListener('DOMContentLoaded', () => {
    document.body.addEventListener('click', (event) => {
        window.electronAPI.showPopupMenu(event.x, event.y);
    });
});

menu.js:

const {Menu} = require('electron')

module.exports = function (win) {
    const template = [
        {
            label: '菜单1',
            submenu: [
                {
                    label: '子菜单1'
                },
                {
                    label: '子菜单2',
                }
            ]
        },

        {
            label: '菜单2',
            submenu: [
                {
                    label: '子菜单1'
                },
                {
                    label: '子菜单2',
                    submenu: [
                        {
                            label: '孙菜单1'
                        },
                        {
                            label: '孙菜单2'
                        }
                    ]
                }
            ]
        },

        {
            label: '帮助',
            role: 'help',
            click() { require('electron').shell.openExternal('https://example.com/help') }
        }
    ]
    return template;
}

index.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./style.css">
    <title>GGBond勇猛无敌</title>
</head>
<body>
    <h1>Hello GGBond</h1>
    <script src="./src/renderer.js"></script>
</body>
</html>

相关文章:

  • 墨迹天气携手天润融通,用AI提升气象服务效率
  • Anolis服务器Arm64架构服务器配置(其他版本服务器解决方式思路一质)
  • 18.分布式任务调度
  • 进程控制 ─── linux第15课
  • 信号量(Semaphore)和文件锁(File Lock)
  • 第六章 流量特征分析-钓鱼邮件
  • RT-thread的MultiButton按键库的使用
  • Windows 系统下 Android 开发常用快捷键‌的整理
  • CentOS 7.9 上安装 Docker Compose
  • cmake、CMakeLists.txt、make、ninja
  • 【Flink银行反欺诈系统设计方案】5.反欺诈系统全生命周期设计
  • 深入浅出:UniApp 从入门到精通全指南
  • nacos和Eureka的学习
  • python量化交易——金融数据管理最佳实践——使用qteasy大批量自动拉取金融数据
  • 《谈判力》核心原则解读
  • “RStudio UI“快速指南
  • MiniMind用极低的成本训练属于自己的大模型
  • 前后分离文件上传案例,前端HTML,后端Net6开发的webapi(完整源代码)下载
  • 【 <一> 炼丹初探:JavaWeb 的起源与基础】之 Servlet 与 JSP 的协作:MVC 模式的雏形
  • 《ARM64体系结构编程与实践》学习笔记(五)
  • 波兰总统选举投票开始,将是对亲欧路线的一次严峻考验
  • 福建、广西等地有大暴雨,国家防总启动防汛四级应急响应
  • 广东缉捕1名象牙走私潜逃非洲“红通”逃犯
  • 缅甸发生5.0级地震
  • 光速晋级!2025年多哈世乒赛孙颖莎4比0战胜对手
  • 北方将现今年首场大范围高温天气,山西河南山东陕西局地可超40℃