Electron开发笔记----前端小白开发桌面应用之路(三)顶部菜单和右键菜单的创建

顶部菜单的编辑


桌面应用的菜单是我们很常用的一个功能,大到各种应用,小到一个小小的记事本,都有自己的菜单,那么接下来就看看如何实现一个菜单

传入模板生成菜单

Electron提供了一个模块Menu来实现菜单功能,我们直接通过一个demo来演示

首先创建如图的目录
文件目录
这里menu文件夹下的menu.js是用来写菜单的信息,包括菜单的内容,以及菜单的点击事件触发的回调,这里实际上是在主进程中调用的,但是为了可以区分功能和复用,我写在menu.js中,并在主进程中require它

主进程index.js

const { app, BrowserWindow } = require('electron')
    // app:管理Electron应用程序的声明周期
    // BrowserWindow负责创建窗口

let mainWindow = null // 要打开的主窗口应用

app.on('ready', () => { // 监听准备事件
    mainWindow = new BrowserWindow({ // 传入对象设置窗口参数,这里设置了宽和高以及允许使用node的API
        width: 500,
        height: 500,
        webPreferences: {
            nodeIntegration: true
        }
    })
    require('./menu/menu.js')
    mainWindow.loadFile('index.html')
    mainWindow.on('closed', () => { // 监听关闭时间
        mainWindow = null // 将窗口置为null,否则的话内存会一直被占用
    })
})

这里menu.js也不做过多解释了,其实看代码也已经很清晰了,唯一要说的是setApplicationMenu方法,它在macOS环境和Windows/Linux环境中的表现不同,在macOS中,会把传入的菜单menu设置为应用程序菜单,而在Windows/Linux中,menu会被设置为每个应用的顶部菜单。
menu.js

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

let template = [{
    label: '文件',
    submenu: [{
            label: '新建窗口',
            click: () => { // 添加点击对应的操作,这里为打开新的窗口
                let newWin = new BrowserWindow({
                    width: 500,
                    height: 500,
                    webPreferences: {
                        nodeIntegration: true
                    }
                })
                newWin.loadFile('index2.html')
                newWin.on('closed', () => {
                    newWin = null
                })
            }
        },
        { label: '关闭窗口' }
    ]
}, {
    label: '编辑',
    submenu: [
        { label: '撤销' },
        { label: '恢复' }
    ]
}]

const menu = Menu.buildFromTemplate(template) // 根据template生成一个Menu对象

Menu.setApplicationMenu(menu) // 根据menu生成菜单

菜单窗口
点击文件中的新建窗口,就可以打开一个新的桌面应用

通过添加MenuItem来创建菜单

除了使用模板的形式外,我们还可以通过往Menu对象中添加MenuItem来添加新的菜单项,因为Menu.buildFromTemplate会返回一个Menu对象,所以这里我们在menu.js中添加

const menu = Menu.buildFromTemplate(template) // 根据template生成一个Menu对象

menu.append(new MenuItem({label:'click',click(){console.log('click')}})) // 在menu中添加新的菜单项

Menu.setApplicationMenu(menu) // 根据menu生成菜单

然后运行该应用,点击click,发现控制台打印出了click
窗口图片
控制台打印
这种做法的好处在于,我们可以动态地修改我们的菜单项,这样根据使用应用的人权限不同,我们可以提供不同的菜单项比如下面这个小demo,我在global.json中添加了identity属性,然后在menu.js中通过读取该配置文件identity属性对应的值,提供不同的菜单项,代码如下
我在根目录下添加了一个VIP.html文件,在点击新创建的菜单项时打开这个页面
global.json

{
    "name":"前端小白",
    "color":"black",
    "identity":"VIP"
}

VIP.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>VIP</title>
</head>
<body>
    <p>欢迎金主爸爸</p>
</body>
</html>

menu.js

const { Menu, BrowserWindow, MenuItem, Notification} = require('electron')
const fs = require('fs')

let template = [{
    label: '文件',
    submenu: [{
            label: '新建窗口',
            click: () => {
                let newWin = new BrowserWindow({
                    width: 500,
                    height: 500,
                    webPreferences: {
                        nodeIntegration: true
                    }
                })
                newWin.loadFile('index2.html')
                newWin.on('closed', () => {
                    newWin = null
                })
            }
        },
        { label: '新建文件' }
    ]
}, {
    label: '编辑',
    submenu: [
        { label: '撤销' },
        { label: '恢复' }
    ]
}]

const menu = Menu.buildFromTemplate(template) // 根据template生成一个Menu对象

const { identity } = JSON.parse(fs.readFileSync('global.json'))

if(identity === "VIP"){
    menu.append(new MenuItem({label:'会员点击',click(){
        let newWin = new BrowserWindow({
            width: 500,
            height: 500,
            webPreferences: {
                nodeIntegration: true
            }
        })
        newWin.loadFile('VIP.html')
        newWin.on('closed', () => {
            newWin = null
        })
    }}))
}

Menu.setApplicationMenu(menu) // 根据menu生成菜单

点击之后我们可以看到,打开了一个新的页面
VIP页面图片
金主爸爸的好感度直线上升,又能继续给我们氪金了

而如果global.json中identity的值不为VIP,那么就不会显示这个菜单项

{
    "name":"前端小白",
    "color":"black",
    "identity":"非VIP"
}

在这里插入图片描述

MenuItem的常见属性

实际上上面已经用到了好几个MenuItem的属性,即使是通过使用传入模板然后使用buildFromTemplate的方法,模板中的对象也和传入MenuItem的参数一致,这里说说MenuItem的几个常见的属性

  • label:菜单项的内容
    • 类型:string
    • 可选
  • click:点击菜单项时对应的操作
    • 类型:function
    • 可选
  • submenu:子菜单数组,该属性对应于type:submenu,设置了该属性后,type:submenu 可以被忽略,如果设置了其他type,那么submenu无效
    • 类型:数组成员为MenuItem的传入对象的数组
    • 可选
  • type:菜单类型
    • 类型:string
    • 可选
    • 可填的值有
      • normal:普通菜单
      • separator:分隔线,和label不能一起使用,否则会不显示内容
      • submenu:有子菜单的菜单
      • checkbox:可选菜单,与checked共用,checked为true时前面有个√
      • radio:单选,也对应于checked
  • role:指定菜单项的行为,使用click属性后该属性会无效,该属性对应于一些electron默认的行为,详情可见文档
    • 类型:string
    • 可选

右键菜单的编辑


右键菜单的编辑,其实和顶部菜单的编辑差不多,只是调用了Menu的另一个API:popup

Menu.popup

首先,我们看一下这个API

  • 功能:将该Menu实例中添加的菜单项作为 browserWindow 中的上下文菜单弹出,browserWindow为传入的对象的一个属性值
  • 参数:对象
    • browserWindow: BrowserWindow对象(可选)为要打开上下文菜单的窗口,默认为当前窗口
    • x:number(可选)为打开菜单离窗口左边界的距离,默认为鼠标当前的位置,如果设置该值,则必须设置y,否则无效
    • y:number(可选)为打开菜单离窗口上边界的距离,默认为鼠标当前的位置,如果设置该值,则必须设置x,否则无效
    • positioningItem:number(可选)在鼠标光标下方的指定坐标处定位的菜单项的索引。默认值为-1 在macOS下使用,我没尝试过所以直接用了官方的解释
    • callback:function(可选)回调函数,触发操作后的回调

右键打开菜单示例

这里的默认打开窗口,也可以通过romate拿到主线程的getCurrentWindow方法来得到当前窗口对应的BrowserWindow对象

const { remote } = require('electron')
const { BrowserWindow, Menu, MenuItem } = remote
window.addEventListener('load',()=>{
	const menu = new Menu() // 创建一个Menu对象
    menu.append(new MenuItem({ label: 'MenuItem1', click() { console.log('item 1 clicked') } })) // 添加MenuItem菜单项

    window.addEventListener('contextmenu', (e) => {
        e.preventDefault()
        menu.popup({ window: remote.getCurrentWindow() })
    }, false)
})

这里要注意的是,我们需要打开控制台才能看到打印的内容,但是这里因为我们使用了自己的顶部菜单,所以也无法使用ctrl+shift+i来打开控制台,这里需要把控制台添加到我们的菜单中,也可以直接添加在右键点击菜单里

const menu = new Menu() // 创建一个Menu对象
menu.append(new MenuItem({ label: 'MenuItem1', click() { console.log('item 1 clicked') } })) // 添加MenuItem菜单项
menu.append(new MenuItem({ label: '打开开发者工具', role: 'toggledevtools' }))
window.addEventListener('contextmenu', (e) => {
    e.preventDefault()
    menu.popup({ window: remote.getCurrentWindow() })
}, false)

右键打开控制台,再右键点击MenuItem1就可以看到控制台打印内容了
右键点击打印

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值