在代码清单中,我们修改了getFileFromUser(),将对窗口的引用作为参数。我避免命名参数窗口,因为它可能与浏览器中的全局对象混淆。在用户选择了一个文件之后,除了文件路径之外,我们还将targetWindow传递给openFile(),如下所示。
列表5.5 重构openFile()以处理特定的窗口: ./app/main.js
const openFile = exports.openFile = (targetWindow, file) => { // 接受对浏览器窗口对象的引用 const content = fs.readFileSync(file).toString(); targetWindow.webContents.send('file-opened', file, content); // 将文件的内容发送到提供的浏览器窗口 }; 将对当前窗口的引用传递给主进程从文件系统读取文件内容之后,我们将文件的路径和内容作为第一个参数传入并发送到窗口。这就提出了一个问题:我们如何获得对窗口的引用。
使用remote模块从渲染器进程调用getFileFromUser(),以便与主进程通信。正如我们在前一章中看到的,remote模块包含对所有模块的引用,否则这些模块只对主进程可用。原来remote还有一些其他方法,尤其是remote还有一些其他方法,尤其是remote.getCurrentWindow(),它返回对调用它的BrowserWindow实例,如下所示。
列表5.6 在渲染器进程中获取对当前窗口的引用: ./app/renderer.js
const currnetWindow = remote.getCurrentWindow();现在我们有了对窗口的引用,完成该特性的最后一步是将它传递给getFileFromUser()。这让主进程中的函数知道它们正在使用的是什么浏览器窗口。
openFileButton.addEventListener('click', () => { mainProcess.getFileFromUser(currnetWindow); });当我们在第三章中为UI实现Markup时,我们包括了一个New File按钮。我们现在在主进程中实现并导入createWindow()函数,我们也可以很快地把那个按钮连接起来。
列表5.8 向newFileButton添加监听器: ./app/renderer.js
newFileButton.addEventListener('click', ()=> { mainProcess.createWindow(); })我们可以在主进程中对多个窗口的实现做一些增强,但是我们已经完成了本章的渲染器进程。下面是app/renderer.js中文件的所有代码。
列表5.9 newFileButton在渲染器进程中的实现: ./app/renderer.js
const { remote, ipcRenderer } = require('electron'); const mainProcess = remote.require('./main.js') const currnetWindow = remote.getCurrentWindow(); const marked = require('marked'); const markdownView = document.querySelector('#markdown'); const htmlView = document.querySelector('#html'); const newFileButton = document.querySelector('#new-file'); const openFileButton = document.querySelector('#open-file'); const saveMarkdownButton = document.querySelector('#save-markdown'); const revertButton = document.querySelector('#revert'); const saveHtmlButton = document.querySelector('#save-html'); const showFileButton = document.querySelector('#show-file'); const openInDefaultButton = document.querySelector('#open-in-default'); const renderMarkdownToHtml = (markdown) => { htmlView.innerHTML = marked(markdown, { sanitize: true }); }; markdownView.addEventListener('keyup', (event) => { const currentContent = event.target.value; renderMarkdownToHtml(currentContent); }); newFileButton.addEventListener('click', () => { mainProcess.createWindow(); }); openFileButton.addEventListener('click', () => { mainProcess.getFileFromUser(currentWindow); }); ipcRenderer.on('file-opened', (event, file, content) => { markdownView.value = content; renderMarkdownToHtml(content); }); 改进创建新窗口的体验在实现上一章中的事件监听器之后单击new File按钮,您可能会对它是否正常工作感到困惑。您可能已经注意到窗口周围的阴影变暗了,或者您可能单击并拖动了新窗口,并显示了下面的前一个窗口。
我们现在遇到的一个小问题是,每个新窗口都出现在与第一个窗口相同的默认位置,并且完全遮住了它。更明显的是,如果新窗口与前一个窗口稍微偏移,就会创建新窗口,如图5.4所示。这个清单显示了如何偏移窗口。
清单5.10 基于当前焦点窗口偏移新窗口: ./app/main.js
const createWindow = exports.createWindow = () => { let x,y; const currentWindow = BrowserWindow.getFocusedWindow(); //获取当前活动的浏览器窗口。 if(currentWindow) { //如果上一步中有活动窗口,则根据当前活动窗口的右下方设置下一个窗口的坐标 const [ currentWindowX, currentWindowY ] = currentWindow.getPosition(); x = currentWindowX + 10; y = currentWindowY +10; } let newWindow = new BrowserWindow({ x, y, show: false, webPreferences: { // WebPreferences中的nodeIntegrationInWorker选项设置为true nodeIntegration: true } }); //创建新窗口,首先使用x和y坐标隐藏它。如果上一步中代码运行了,则设置这些值;如果没有运行,则未定义这些值,在这种情况下,将在默认位置创建窗口。 newWindow.loadFile('app/index.html'); newWindow.once('ready-to-show', () => { newWindow.show(); }); newWindow.on('closed', () => { windows.delete(newWindow); newWindow = null; }); windows.add(newWindow); return newWindow; };