electron自定义窗口和右键菜单样式

发布于:2024-04-26 ⋅ 阅读:(17) ⋅ 点赞:(0)

前言

electron默认沿用系统UI,并没有提供很多接口供使用者定制样式,如果想要完全自定义的样式,目前我能想到的方案只能是通过前端自定义样式,然后通过进程通信来实现系统基础功能:最大/小化、关闭、拖动窗口等。

效果图:

在这里插入图片描述

在这里插入图片描述


一、窗口自定义

通过前面系列文章我们可以了解到,窗口是通过实例化BrowserWindow对象创建的,实例化时会传入一些窗口的参数。

要实现窗口自定义,就必须把窗口默认样式都屏蔽。几个关键的参数如下:

transparent: true,
backgroundColor:'rgba(0,0,0,0)',
frame:false,

参数含义很好理解,窗体透明无边框,参数详解请查询官网。

把系统自带的窗体样式去掉后,我们会得到一个只有主体的窗口,这个主体就是前端(vue)渲染的窗口,我们可以通过控制它,来实现任何样式的窗口。

但是这会带来一个问题,那就是窗口对象提供的很多便捷功能无法使用了,所以如果需要做最大化、最小化、拖动窗口等功能,只能通过进程通信,前端发送指令,主进程接收指令后,完成相应功能。具体原理请参考本系列前面关于进程通信的文章。

这里简单列一个示例代码,以最大化为例:

//vue代码部分,在某个dom元素上监听事件
<img class="title-icon" src="@/assets/img/maxsize.png"  alt="最大化" @click="handleMaxSize()">

 function handleMaxSize(){
        renderApi.handleMaxSize()
    }
//preload.js中定义通信的api,下面是我项目中所有渲染进程到主进程的通信

const handleSendPageName= (pageName) => ipcRenderer.send('send-page-name', pageName) //渲染进程主动到主进程
const handleMinSize= () => ipcRenderer.send('send-min-size') //渲染进程主动到主进程
const handleMaxSize= () => ipcRenderer.send('send-max-size') //渲染进程主动到主进程
const handleRestore= () => ipcRenderer.send('send-restore') //渲染进程主动到主进程
const handleRelaunch= () => ipcRenderer.send('user-click-Dock-Icon') //渲染进程主动到主进程

const handleCloseWin=()=>{
    ipcRenderer.send('send-auto-close')
}

contextBridge.exposeInMainWorld('renderApi', {
    //监听渲染进程事件
    handleGetStoreFiles,
    handleSendPageName,
    handleMinSize,
    handleMaxSize,
    handleCloseWin,
    handleRestore,
    handleRelaunch
})
//主进程main.js中接收对应的通信
     ipcMain.on('send-max-size', () => {
            if(win.isMaximized()){
                win.unmaximize()
            }else{
                win.maximize()
            }
        })

至此,模拟窗口最大化功能的全部过程就打通了。

二、窗口拖拽功能

这里值得注意的是,拖拽窗口不止是要配置参数,还要给对应dom元素增加类。

比如说我想实现拖动类名为“c-drag”的元素时,拖动窗口移动,大致代码如下:

<div class="c-drag">

 </div>
.c-drag{
  -webkit-app-region: drag;
}

-webkit-app-region: drag是electron提供的css样式,具体可查询官网。

这是一个比较小众的知识点,网上资料目前较少,这里记录一下。

三、不同窗口设置不同大小

这一部分逻辑略微复杂。

窗口大小的设置一定是在主进程中设置,如果仅仅依靠vue部分控制显示区域大小,不显示区域设置为透明,虽然视觉上可以实现不同的窗体大小,但是这是一种伪实现,因为透明部分只是人眼看不到而已,鼠标点击、拖拽等功能仍然存在,就会对软件用户造成困扰。

在主进程中设置窗口大小,最重要的就是进入不同页面时,要主动向主进程发送指令,并告诉主进程,我现在进入登录页了,我现在进入正常页了,我现在进入xx页……

主进程接收指令后,根据参数,控制窗口的大小即可。

在我的项目中,各页面有一个统一的路由跳转方法,所以我在跳转路由后,同时将活跃页面的name通handleSendPageName发送给主进程。代码如下:

function turnToPage_menu(name) {
  console.log(name)
  turnToPage(router, name)

  renderApi.handleSendPageName(activeName.value) //发送pageName到主进程,以此判断窗口大小

}

主进程接收到指令后,根据参数,决定窗口设置为多大,代码:

ipcMain.on('send-page-name', (event, pageName) => {
            console.log('setWindowSize',pageName)
            const loginSize={
                width:500,
                height:580
            }
            const pageSize={
                width:1000,
                height: 950
            }
            if (pageName && pageName == 'normalLogin') {
                win.setSize(loginSize.width, loginSize.height)
                win.center()
                win.setMenuBarVisibility(false)

            } else {
                if(this.judgePageSet(win,pageSize,loginSize)){
                    win.setSize(pageSize.width, pageSize.height)
                    win.center()
                    win.setMenuBarVisibility(true)
                }

            }
        })

至此不同页面实现不同大小的窗口功能,就实现了。

其实在我们项目中,还有另一种需求场景,那就是当通过注册表把软件注册到系统右键后,上传文件时,右下角有一个简易窗口,窗口高度根据上传文件数量来计算。这就需要判断命令行参数、获取文件下载地址等操作,更加复杂,但是应用场景应该不多,有兴趣的同学可以私聊,此处不再赘述。

四、右键菜单自定义

我并没有在实际项目中真正实现过右键菜单的自定义,但是道理和窗口是相通的,如果electron提供的右键菜单样式无法满足要求,那就舍弃框架提供的便捷菜单,通过进程间通信,手动实现。

总结

  1. 舍弃系统窗口所有功能,利用通信机制,模拟实现需要的窗口功能。
  2. 判断页面发送的参数,为不同页面的窗口设置不同的样式。

博主的前端资源: