窗口通信与拓展屏

本期内容次要介绍拓展屏的开启以及两个窗口之间的音讯通信。

拓展屏

在窗口启动这一节里咱们封装了一个createWindow,用这个来创立一个窗口,拓展屏也是窗口,只不过咱们依据有几个显示屏把其放在对应的外接屏上罢了。

渲染过程

咱们在渲染过程增加一个按钮,让其点击之后,向主过程发送对拓展屏的操作(关上或暗藏)

<a-button type="primary" @click="openScreen">关上拓展屏</a-button>const state = reactive({  open: true,  message: ''})async function openScreen() {  await window.ipcRenderer.invoke('win-subScreen', {    open: state.open,    path: '#/subScreen'  })  state.open = !state.open}

主过程

主过程承受到之后,通过screen.getAllDisplays()获取当初窗口的数组(有几个显示屏),而后依据externalDisplay来获取咱们外接屏幕的信息。
同样的,咱们创立新的窗口也是用createWindow,之前咱们渲染过程传递了一个path,这个path呢就是咱们拓展屏要展现的页面。
这里须要留神的是createWindow返回的值须要用一个全局变量保存起来,之前托盘的时候说过,如果是局部变量的话,在函数执行结束之后会被销毁,那么窗口也就被销毁了。
咱们这里用global赋值是前面窗口通信会用到,你这里用全局变量赋值时一样的。

global.jsglobal.tray = nullglobal.willQuitApp = falseglobal.envConfig = {}global.sharedObject = {  win: '',  subScreen: ''}export default globalimport { ipcMain, app, screen } from 'electron'import global from '../config/global'import createWindow from './createWindow'import path from 'path'const win = global.sharedObject.winipcMain.handle('win-subScreen', (_, data) => {  if (data.open) {    const displays = screen.getAllDisplays()    const mainBounds = win.getNormalBounds()    const externalDisplay = displays.find((display) => {      return display.bounds.x !== 0 || display.bounds.y !== 0    })    if (externalDisplay) {      if (global.sharedObject.subScreen) {        global.sharedObject.subScreen.show()      } else {        global.sharedObject.subScreen = createWindow({          frame: false,          show: false,          parent: win, // win是主窗口          fullscreen: true,          webPreferences: {            webSecurity: false,            contextIsolation: false,            enableRemoteModule: true,            nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,            plugins: true,            preload: path.join(__dirname, 'preload.js'),            devTools: false          },          x: mainBounds.x < 0 && Math.abs(mainBounds.x) > (win.getContentSize()[0] / 2) ? 0 : externalDisplay.bounds.x,          y: externalDisplay.bounds.y        }, data.path, `index.html${data.path}`)        global.sharedObject.subScreen.once('ready-to-show', () => {          global.sharedObject.subScreen.show()        })        global.sharedObject.subScreen.on('closed', () => {          global.sharedObject.subScreen = null        })      }    } else {      console.log('未检测到拓展屏')    }  } else {    global.sharedObject.subScreen && global.sharedObject.subScreen.destroy()  }})

这里呢有个小解决,咱们先判断一下咱们的软件是位于那个屏幕,比方咱们有两个屏,1和2,那么咱们的主窗口和拓展屏应该处于不同地位,比方:咱们的主窗口在1的话,那么咱们的拓展屏应该在2关上,如果地位雷同的话,因为咱们拓展屏设置的是全屏,此时会把主窗口给遮挡住,如果没有设置快捷键敞开的话,那么将无奈敞开拓展屏,故有以下解决。

const mainBounds = win.getNormalBounds()mainBounds为主窗口信息,如果主窗口有一半以上都处于副屏幕的话,那么咱们认为主窗口在副屏,那么拓展屏关上地位应该在主屏,否则的话应该在副屏。x: mainBounds.x < 0 && Math.abs(mainBounds.x) > (win.getContentSize()[0] / 2) ? 0 : externalDisplay.bounds.x

窗口通信

electorn的窗口间的通信呢,一般来说有两种形式。

  1. 窗口A渲染过程发送信息到主过程,主过程收到后发送信息给窗口B的渲染过程,也就是用主过程做个直达。
  2. 窗口A渲染过程中通过窗口B的WebContents.id间接发送信息给窗口B。

通信形式

这里介绍几种常见的通信形式

ipcRenderer.invoke:渲染过程发送音讯给主过程,这是一个promise,能够在其resolve中获取ipcMain.handle的返回值ipcMain.handle:接管invoke发送的信息,能够return值给ipcRenderer.invokeipcRenderer.send:渲染过程发送音讯给主过程ipcMain.on:接管send的信息ipcRenderer.on:接管主过程的音讯webContents.send:主过程发送音讯给渲染过程ipcRenderer.sendTo:能够通过webContentsId间接发送信息到对应的渲染过程窗口

咱们这里把1,2都实现一下

渲染过程

渲染过程中能够通过remote来获取主过程中global的值,remote.getGlobal('sharedObject').subScreen就是咱们之前拓展屏的窗口。
transferSub是咱们的计划1,directSub是计划2。

div class="subMessage">  <a-textarea    v-model:value="state.message"    :auto-size="{ minRows: 2, maxRows: 5 }"  />  <a-button type="primary" :disabled="state.message.length === 0" @click="transferSub">直达发送</a-button>  <a-button type="primary" :disabled="state.message.length === 0" @click="directSub">间接发送</a-button></div>import { defineComponent, reactive, onMounted, onUnmounted, getCurrentInstance } from 'vue'const { remote } = require('electron')const state = reactive({  open: true,  message: ''})const { proxy } = getCurrentInstance()const { $message } = proxyfunction transferSub() {  window.ipcRenderer.invoke('win-subScreen-message', state.message)}function directSub() {  const subScreen = remote.getGlobal('sharedObject').subScreen  if (subScreen) {    window.ipcRenderer.sendTo(subScreen.webContents.id, 'main-subScree', state.message)  }}onMounted(() => {  window.ipcRenderer.on('subScree-main', (_event, data) => {    $message.success(data)  })})onUnmounted(() => {  window.ipcRenderer.removeListener('subScree-main')})

主过程

直达一下,用webContents.send把信息发送给拓展屏窗口

ipcMain.handle('win-subScreen-message', (_, data) => {  if (global.sharedObject.subScreen) {    global.sharedObject.subScreen.webContents.send('renderer-subScreen-message', data)  }})

拓展屏信息接管及发送

咱们这里间接监听直达的音讯以及间接发送的音讯,再间接告诉主窗口音讯。

<template>  <div class="subScreen">{{ state.message }}</div></template><script>import { defineComponent, reactive, onMounted, onUnmounted } from 'vue'const { remote } = require('electron')export default defineComponent({  setup() {    const state = reactive({      message: ''    })    onMounted(() => {      const win = remote.getGlobal('sharedObject').win      window.ipcRenderer.on('renderer-subScreen-message', (_event, data) => {        state.message = data        window.ipcRenderer.sendTo(win.webContents.id, 'subScree-main', '我收到了直达发送信息')      })      window.ipcRenderer.on('main-subScree', (_event, data) => {        state.message = data        window.ipcRenderer.sendTo(win.webContents.id, 'subScree-main', '我收到了间接发送信息')      })    })    onUnmounted(() => {      window.ipcRenderer.removeListener('renderer-subScreen-message')      window.ipcRenderer.removeListener('main-subScree')    })    return {      state    }  }})</script>

验证

咱们关上拓展屏,在主窗口输出信息,点击间接发送或者直达发送,看看拓展窗口的显示是否为咱们输出的信息,主窗口展现拓展屏的告诉信息。

本系列更新只有利用周末和下班时间整顿,比拟多的内容的话更新会比较慢,心愿能对你有所帮忙,请多多star或点赞珍藏反对一下

本文地址:链接
本文github地址:链接