Electron Cross-Platform Development Practice - chapter08 - Deep Integration (shell module), Dynamic Launch Menu Item

Catalog

Use shell module in rendering process (UI interface)

//Open in File Manager
const { ..., shell } = require('electron');

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

//Open with default program
const openInDefaultApplication = () => {
    if (!filePath) { return alert('This file has not been saved to the file system.'); }
    //electron-9.x no longer exists
    shell.openItem(fullPath)
};

Use shell module in application menu

  • 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);

Use shell module in context menu

  • renderer.js

//Context Menu
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();
});

Disable Menu Items

!!

  !! Often used to make type judgments, after the first step! (variable) do logical negation, in js novices often write bloated code like this:

Determines that variable a is not empty, undefined, or a non-empty string to execute the contents of a method body

var a;
if(a!=null&&typeof(a)!=undefined&&a!=''){
    //a Code executed with content  
}

In fact, we just need to write a judgment:

if(!!a){
    //a Code to execute only if there is content...  
}

You can achieve the same effect as above.A is a variable with real meaning to execute the method, otherwise variables null, undefined, and''empty strings will not execute the following code.
Can be summarized;
"!"Is logic and operation, and can be logic with any variable and converted to a Boolean value, "!!" is the logic and inversion operation, especially the latter is concise and efficient in determining the type, eliminating redundant code to determine null, undefined and empty strings many times.

Dynamic Generation Menu

Disable menu: enabled:!! filePath

Dynamic startup menu: 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();
});

Application Menu

What makes the application menu different from the context menu?

  • Unlike the context menu, the application menu uses only one and is shared across all windows
  • In a macOS system, even if the application does not have a window open.
  • The application menu exists in the main process and does not have access to the rendering process variable (this variable is related to menu item disablement)

Create Application Menu (Dynamic Processing Enabled and Disabled)

Main points:

    const hasOneOrMoreWindows = !!BrowserWindow.getAllWindows().length; //Is there a window open
    const focusedWindow = BrowserWindow.getFocusedWindow(); //Get the current focus window without returning null
    const hasFilePath = !!(focusedWindow && focusedWindow.getRepresentedFilename());  //focusedWindow.getRepresentedFilename() Return to the file opened by the window

focusedWindow.getRepresentedFilename() Only applicable to darwin platform

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

const createApplicationMenu = () => {
    const hasOneOrMoreWindows = !!BrowserWindow.getAllWindows().length; //Is there a window open
    const focusedWindow = BrowserWindow.getFocusedWindow(); //Get the current focus window without returning null
    const hasFilePath = !!(focusedWindow && focusedWindow.getRepresentedFilename());  //focusedWindow.getRepresentedFilename() Return to the file opened by the window

    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

When the program starts

app.on('ready', () => {
    //Menu.setApplicationMenu(applicationMenu); //Set application menu
    createApplicationMenu();//Set Application Menu
    mainWindow = createwindow();
});

When opening and closing a new window

    newWindow.on('focus', () => {
        createApplicationMenu(); //Create a new application menu when the window gets focus
    });

    newWindow.on('closed', () => {
        ...
        createApplicationMenu();//Create a new application menu when closing a window
    })

When opening a new file

const openFile = exports.openFile = (targetWindow, file) => {
   ...
    createApplicationMenu(); //Create a new application menu when opening a new file
};

Keywords: shell Windows

Added by kentlim04 on Thu, 25 Jun 2020 03:04:27 +0300