08.《Electron 跨平台开发实战》- chapter08-深入集成(shell模块)、动态启动菜单项

别来无恙 提交于 2020-08-05 12:05:12

在渲染进程(UI界面)中使用shell模块

//在文件管理器中打开
const { ..., shell } = require('electron');

const showFile = () => {
    if (!filePath) { return alert('This file has not been saved to the file system.'); }
    shell.showItemInFolder(filePath);
};

//使用默认程序打开
const openInDefaultApplication = () => {
    if (!filePath) { return alert('This file has not been saved to the file system.'); }
    //electron-9.x 已经不存在了
    shell.openItem(fullPath)
};

在应用菜单中使用 shell 模块

  • applicationMenu.js
 { type: 'separator' },
        {
          label: 'Show File',
          enabled: hasFilePath,
          click(item, focusedWindow) {
            if (!focusedWindow) {
              return dialog.showErrorBox(
                'Cannot Show File\'s Location',
                'There is currently no active document show.'
              );
            }
            focusedWindow.webContents.send('show-file');
          },
        },
        {
          label: 'Open in Default Application',
          enabled: hasFilePath,
          click(item, focusedWindow) {
            if (!focusedWindow) {
              return dialog.showErrorBox(
                'Cannot Open File in Default Application',
                'There is currently no active document to open.'
              );
            }
            focusedWindow.webContents.send('open-in-default');
          },
        },
  • renderer.js
ipcRenderer.on('show-file', showFile);
ipcRenderer.on('open-in-default', openInDefaultApplication);

在上下文菜单中使用 shell 模块

  • renderer.js

//上下文菜单
const markdownContextMenu = Menu.buildFromTemplate([
...
    {
        label: 'Show File in Folder',
        click: showFile,
        enabled: !!filePath
      },
      {
        label: 'Open in Default',
        click: openInDefaultApplication,
        enabled: !!filePath
      },
    { type: 'separator' },
   ...
]);

markdownView.addEventListener('contextmenu', (event) => {
    event.preventDefault();
    markdownContextMenu.popup();
});

禁用菜单项

!!

  !! 常常用来做类型判断,在第一步!(变量)之后再做逻辑取反运算,在js中新手常常会写这样臃肿的代码:

判断变量a为非空,未定义或者非空串才能执行方法体的内容

var a;
if(a!=null&&typeof(a)!=undefined&&a!=''){
    //a有内容才执行的代码  
}

实际上我们只需要写一个判断表达:

if(!!a){
    //a有内容才执行的代码...  
}

就能和上面达到同样的效果。a是有实际含义的变量才执行方法,否则变量null,undefined和''空串都不会执行以下代码。
可以总结出来;
“!”是逻辑与运算,并且可以与任何变量进行逻辑与将其转化为布尔值,“!!”则是逻辑与的取反运算,尤其后者在判断类型时代码简洁高效,省去了多次判断null、undefined和空字符串的冗余代码。

动态生成菜单

禁用菜单:enabled: !!filePath

动态启动菜单:createContextMenu().popup();

const createContextMenu = () => {
    return Menu.buildFromTemplate([
        { label: 'Open File', click() { mainProcess.getFileFromUser(); } },
        {
            label: 'Show File in Folder',
            click: showFile,
            enabled: !!filePath
        },
        {
            label: 'Open in Default',
            click: openInDefaultApplication,
            enabled: !!filePath
        },
        { type: 'separator' },
        { label: 'Cut', role: 'cut' },
        { label: 'Copy', role: 'copy' },
        { label: 'Paste', role: 'paste' },
        { label: 'Select All', role: 'selectall' },
    ]);
};

markdownView.addEventListener('contextmenu', (event) => {
    event.preventDefault();
    createContextMenu().popup();
});

应用菜单

应用菜单不同于上下文菜单的特殊点:

  • 与上下文菜单不同,应用菜单只用一个,所有窗口共享
  • 在macOS系统中,甚至应用没有打开窗口的情形。
  • 应用菜单存在于主进程中,访问不到渲染进程的变量(这个变量跟菜单项禁用相关)

创建应用菜单(动态处理启用和禁用状态)

要点:

    const hasOneOrMoreWindows = !!BrowserWindow.getAllWindows().length; //是否有窗口打开
    const focusedWindow = BrowserWindow.getFocusedWindow(); //获取当前焦点窗口,无返回null
    const hasFilePath = !!(focusedWindow && focusedWindow.getRepresentedFilename());  //focusedWindow.getRepresentedFilename() 返回窗口打开的文件

focusedWindow.getRepresentedFilename()只适用于darwin平台

  • applicationMenu.js
const { app, BrowserWindow, dialog, Menu } = require('electron');
const mainProcess = require('./main');

const createApplicationMenu = () => {
    const hasOneOrMoreWindows = !!BrowserWindow.getAllWindows().length; //是否有窗口打开
    const focusedWindow = BrowserWindow.getFocusedWindow(); //获取当前焦点窗口,无返回null
    const hasFilePath = !!(focusedWindow && focusedWindow.getRepresentedFilename());  //focusedWindow.getRepresentedFilename() 返回窗口打开的文件

    const template = [
        {
            label: 'File',
            submenu: [
                ...
                {
                    label: 'Save File',
                    accelerator: 'CommandOrControl+S',
                    enabled: hasOneOrMoreWindows,
                    click(item, focusedWindow) {
                        if (!focusedWindow) {
                            return dialog.showErrorBox(
                                'Cannot Save or Export',
                                'There is currently no active document to save or export.'
                            );
                        }
                        focusedWindow.webContents.send('save-markdown');
                    },
                },
                {
                    label: 'Export HTML',
                    accelerator: 'Shift+CommandOrControl+S',
                    enabled: hasOneOrMoreWindows,
                    click(item, focusedWindow) {
                        if (!focusedWindow) {
                            return dialog.showErrorBox(
                                'Cannot Save or Export',
                                'There is currently no active document to save or export.'
                            );
                        }
                        focusedWindow.webContents.send('save-html');
                    },
                },
                { type: 'separator' },
                {
                    label: 'Show File',
                    enabled: hasFilePath,
                    click(item, focusedWindow) {
                        if (!focusedWindow) {
                            return dialog.showErrorBox(
                                'Cannot Show File\'s Location',
                                'There is currently no active document show.'
                            );
                        }
                        focusedWindow.webContents.send('show-file');
                    },
                },
                {
                    label: 'Open in Default Application',
                    enabled: hasFilePath,
                    click(item, focusedWindow) {
                        if (!focusedWindow) {
                            return dialog.showErrorBox(
                                'Cannot Open File in Default Application',
                                'There is currently no active document to open.'
                            );
                        }
                        focusedWindow.webContents.send('open-in-default');
                    },
                },
            ],
        },
       ... 
    ];
   ...
  
    return Menu.setApplicationMenu(Menu.buildFromTemplate(template));
};

module.exports = createApplicationMenu;

  • main.js

程序启动时

app.on('ready', () => {
    //Menu.setApplicationMenu(applicationMenu); //设置应用菜单
    createApplicationMenu();//设置应用菜单
    mainWindow = createwindow();
});

打开和关闭新窗口时

    newWindow.on('focus', () => {
        createApplicationMenu(); //窗口获取焦点时,创建新的应用菜单
    });

    newWindow.on('closed', () => {
        ...
        createApplicationMenu();//关闭一个窗口时,创建新的应用菜单
    })

打开新文件时

const openFile = exports.openFile = (targetWindow, file) => {
   ...
    createApplicationMenu(); //打开新文件时,创建新应用菜单
};
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!